Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do TypeScript conditional types map differently than constrained generics?

I am having no luck understanding why the code below functions as it does:

type MapOverString<T extends string> = { [K in T]: K };

type IfStringMapOverIt<T> = T extends string ? MapOverString<T> : never;

type ThisWorks = MapOverString<'a'>;
// { a: 'a' }

type ThisAlsoWorks = IfStringMapOverIt<'a'>;
// { a: 'a' }

type Union = 'a' | 'b' | 'c';

type ThisWorksToo = MapOverString<Union>;
// { a: 'a', b: 'b', c: 'c' }

type ThisDoesnt = IfStringMapOverIt<Union>;
// MapOverString<'a'> | MapOverString<'b'> | MapOverString<'c'>

Playground link

I must be missing something, because MapOverString and IfStringMapOverIt seem like they should function identically.

Ultimately, I am using string literals and generics to cascade through permutations of configuration types. For example, if you want StringConfig<T> configured with options 'a' | 'b' | 'c':

type ConfigMap<T> = T extends number
  ? NumberConfig
  : T extends string
    ? StringConfig<T>
    : never

type MyConfig = ConfigMap<'a' | 'b' | 'c'> // so many sad faces

Could someone enlighten me? What's going on here?

like image 904
evanbb Avatar asked Oct 24 '20 09:10

evanbb


People also ask

What are conditional types in TypeScript?

Conditional types help describe the relation between the types of inputs and outputs. When the type on the left of the extends is assignable to the one on the right, then you'll get the type in the first branch (the “true” branch); otherwise you'll get the type in the latter branch (the “false” branch).

What are generics in TypeScript when would you use them?

TypeScript Generics is a tool which provides a way to create reusable components. It creates a component that can work with a variety of data types rather than a single data type. It allows users to consume these components and use their own types.

How do you define a generic type in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.

Does TypeScript have generics?

TypeScript fully supports generics as a way to introduce type-safety into components that accept arguments and return values whose type will be indeterminate until they are consumed later in your code.


1 Answers

This is an application of the distribution property of conditional types. A condition over naked type parameter, will trigger this behavior and T extends string satisfies this. You might also see T extend T or T extends any or T extends unknown used for this very reason, just to trigger distribution.

You can read more about distributive conditional types in the handbook

You can disable distribution by using a condition over a tuple [T] extends [string]. The effect of this is similar to a regular condition, just since the type parameter is no longer naked distribution will be displayed.

type StringConfig<T extends string> = { [K in T]: K };
type NumberConfig ={}

type ConfigMap<T> = [T] extends [number]
  ? NumberConfig
  : [T] extends [string]
    ? StringConfig<T>
    : never

export type MyConfig = ConfigMap<'a' | 'b' | 'c'> // so many sad faces
let x:MyConfig = {
  a:'a',
  b:'b',
  c: 'c'
}

Playground Link

like image 90
Titian Cernicova-Dragomir Avatar answered Nov 07 '22 19:11

Titian Cernicova-Dragomir