TypeScript's infer keyword is your secret weapon for effortlessly deducing types, simplifying code, and unlocking the full potential of type inference. In this article, we'll explore how infer can make your TypeScript development smoother and more efficient. ๐
Type inference in TypeScript is used to provide type information when there is no explicit type annotation. This usually occurs when you haven't added types to your variables:
let str = "Hello, World!"; // str is a string here
Everything is fine with this; TypeScript handles it for you. ๐
If you want to try type inference yourself you can use the infer keyword, and it will look like this:
let str = "Hello, World!"; // str is a string
type GetType<T> = T extends infer S ? S : never;
type T1 = GetType<typeof str>; // string
Of course, this example may seem strange and not interesting, but it can give you an idea of how the infer keyword can be used later. ๐ค
As you can see, we used infer within a conditional statement, and this is a limitation of using infer in TypeScript. Also, you cannot use it outside of an extends clause.
If you want to extract the type of an element inside an array, you can do it like this:
let arr = [1,2,3];
type GetTypeOfArrayElement<T> = T extends (infer S)[] ? S : never;
type T1 = GetTypeOfArrayElement<typeof arr>; // number
Here, the infer keyword allows you to extract the type within the array and then use it. It's like defining a reference that you can use later.
That example, also might not seem very interesting because, in the end, you can achieve the same with this approach:
type T2 = typeof arr[0]; // number
However, TypeScript has more complex structures that you cannot work around with simple syntax. Let's consider promises. What if you need to determine the type of the value returned by a promise? Here is where infer can also help you:
let promise = Promise.resolve([1, 2, 3]); // Promise<number[]>
type GetTypeOfPromise<T> = T extends Promise<infer S> ? S : never;
type T1 = GetTypeOfPromise<typeof promise>; // number[]
type T2 = GetTypeOfPromise<Promise<number>>; // number
type T3 = GetTypeOfPromise<Promise<string[]>>; // string[]
By the way, Typescript itself already have a built-in utility for it that has more powerful functionality ๐:
type T4 = Awaited<typeof promise> // number[]
Let's go further. The gaining access to function arguments can be defined like this:
type GetTypeOfFirstArgument<T> = T extends (arg: infer U, ...args) => any ? U : never;
type T1 = GetTypeOfFirstArgument<typeof document.getElementById>; // string
type T2 = GetTypeOfFirstArgument<(arg1: string, arg2: number) => void >; // string
And in the end, maybe the most practical example with React component function.
It can happen that in some libraries, they didn't export the props for their component. But in your code, you really need it. So with infer keyword getting the prop type is possible, and will look like this:
const OutsideComponent: React.FC<{ name: string }> = () => null;
type InferProps<T> = T extends React.FC<infer P> ? P : never;
type Props = InferProps<typeof OutsideComponent>;
This technique allows you to infer the prop types of a React component function even if they aren't explicitly exported
Conclusion
Incorporating infer into your TypeScript toolkit can significantly streamline your code, simplify complex type manipulations, and boost your development productivity. Embrace this powerful keyword to unlock the true potential of type inference in TypeScript. Happy coding! ๐