Conditional Types Flashcards

1
Q

Q: What are conditional types in TypeScript and what is their basic syntax?

A

A: Conditional types allow you to create types that work like if statements, selecting different types based on a condition. They use the syntax: SomeType extends OtherType ? TrueType : FalseType.

interface Animal { live(): void }
interface Dog extends Animal { woof(): void }

type CheckAnimal<T> = T extends Animal ? 'yes' : 'no';
type IsDog = CheckAnimal<Dog>;        // type is 'yes'
type IsRegExp = CheckAnimal<RegExp>;   // type is 'no'
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Q: How do conditional types work with generics and how can they simplify overloads?

A

A: Conditional types become powerful when combined with generics, allowing you to create flexible type relationships and reduce the need for function overloads.

interface IdLabel { id: number; }
interface NameLabel { name: string; }

// Using conditional type instead of multiple overloads
type NameOrId<T extends string | number> = T extends number 
  ? IdLabel 
  : NameLabel;

function createLabel<T extends string | number>(
  idOrName: T
): NameOrId<T> {
  throw "unimplemented";
}

// Usage
let a = createLabel("typescript");  // type: NameLabel
let b = createLabel(123);          // type: IdLabel
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Q: How does the infer keyword work in conditional types?

A

A: The infer keyword allows you to extract and reuse type information from the type you’re checking against. It’s commonly used to extract types from arrays, promises, and functions.

// Extract return type of a function
type GetReturnType<Type> = Type extends (...args: any[]) => infer Return 
  ? Return 
  : never;

// Usage
type Num = GetReturnType<() => number>;    // type: number
type Str = GetReturnType<(x: any) => string>;  // type: string

// Extract array element type
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
type NumberType = Flatten<number[]>;  // type: number
type StringType = Flatten<string>;    // type: string
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Q: What are distributive conditional types and how do they work with unions?

A

A: When conditional types operate on a generic type that is a union, they are automatically distributed over each member of that union. You can prevent this behavior by wrapping types in square brackets.

// Distributive behavior
type ToArray<Type> = Type extends any ? Type[] : never;
type StrNumArr = ToArray<string | number>;  
// type is string[] | number[]

// Non-distributive behavior
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
type StrNumArrNonDist = ToArrayNonDist<string | number>;  
// type is (string | number)[]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Q: How do conditional type constraints work and when should you use them?

A

A: Conditional type constraints allow you to check for the presence of properties or structures before accessing them, providing type-safe ways to work with potentially incomplete types.

// With constraint
type MessageOf<T extends { message: unknown }> = T["message"];

// With conditional type to handle missing properties
type MessageOf2<T> = T extends { message: unknown } 
  ? T["message"] 
  : never;

interface Email { message: string; }
interface Dog { bark(): void; }

type EmailMessage = MessageOf2<Email>;  // type: string
type DogMessage = MessageOf2<Dog>;      // type: never
How well did you know this?
1
Not at all
2
3
4
5
Perfectly