This website collects cookies to deliver better user experience
Constraining literal types with generics in TypeScript
Constraining literal types with generics in TypeScript
Let's say you need an object with the shape of Record<string, { s: string } | { n: number } | { b: boolean }>. You fire up your IDE and write down something like this:
Great, you can now type check your object literals:
const o1:MyObject={ a:{ s:1},// error: Type 'number' is not assignable to type 'string'.};// all goodconst o2:MyObject={ a:{ s:"str"},};
Some time later you decide that you need to know the type of o.a, but it can't be inferred! Due to the MyObject type reference, the object literal type information is lost and all you are left with is A | B | C:
typeT=typeof o2.a;// => A | B | C
Moreover, because string is used as an indexed access type of Record, TS will not warn you about the non-existent property access:
// TS guess: `A | B | C`// Harsh reality: `undefined`const value = o2.j;
The autocomplete is also not available in this case.
Fortunately, we can leverage the power of generics to both type check the object literal and preserve type information:
The extends clause of the type parameter enforces the correct type of the object:
const o =identity({ a:{ s:1},// error: Type 'number' is not assignable to type 'string'.});
At the same time, the literal type information is preserved because the identity function returns exactly what it have received: T, which is the (literal) type of the object literal.