xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

TypeScript Conditional Types All In One

TypeScript Conditional Types All In One

 // SomeType extends OtherType ? TrueType : FalseType;


type SomeType = number;
type OtherType = any;
type TrueType = number;
type FalseType = string;

type Stuff =SomeType extends OtherType ? TrueType : FalseType;
// type Stuff = number

interface IdLabel {
  id: number /* some fields */;
}
interface NameLabel {
  name: string /* other fields */;
}

function createLabel(id: number): IdLabel;
// function createLabel(id: number): IdLabel (+2 overloads)

function createLabel(name: string): NameLabel;
// function createLabel(name: string): NameLabel (+2 overloads)

function createLabel(nameOrId: string | number): IdLabel | NameLabel;
// function createLabel(nameOrId: string | number): IdLabel | NameLabel (+2 overloads)

function createLabel(nameOrId: string | number): IdLabel | NameLabel {
  throw "unimplemented";
}
// function createLabel(id: number): IdLabel (+2 overloads)
// better solution ✅

type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel;

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

Conditional Type Constraints

条件类型约束


interface MessageType {
  message: unknown;
}
// type MessageOf<T> = T extends MessageType ? T["message"] : never;

// 等价于
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
 
interface Email {
  message: string;
}
 
interface Dog {
  bark(): void;
}
 
type EmailMessageContents = MessageOf<Email>;
// type EmailMessageContents = string
 
type DogMessageContents = MessageOf<Dog>;
// type DogMessageContents = never

type Flatten<T> = T extends any[] ? T[number] : T;

//  array, extracts out the element type
type Str = Flatten<string[]>;
// type Str = string
type num = Flatten<number[]>;
// type num = number

// not array, leaves the type alone.
type Num = Flatten<number>;
// type Num = number
type str = Flatten<string>;
// type str = string


Inferring Within Conditional Types

在条件类型中推断

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
// type Flatten<Type> = Type extends (infer Item)[] ? Item : Type



// 可以从函数类型中提取返回类型
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never;

type Num = GetReturnType<() => number>;
// type Num = number

type Str = GetReturnType<(x: string) => string>;
// type Str = string

type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;
// type Bools = boolean[]

type VoidType = GetReturnType<() => void>;
// type VoidType = void

declare function stringOrNum(x: string): number;
declare function stringOrNum(x: number): string;
// 从具有多个调用签名的类型(例如重载函数的类型)进行推断时,从最后一个签名进行推断(这大概是最宽容的包罗万象的情况)
// 无法根据参数类型列表执行重载决议。
declare function stringOrNum(x: string | number): string | number;

// type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

type T1 = ReturnType<typeof stringOrNum>;
// type T1 = string | number

Distributive Conditional Types

可分配条件类型

type ToArray<Type> = Type extends any ? Type[] : never;

// union type / 联合类型
type StrArrOrNumArr = ToArray<string | number>;
// type StrArrOrNumArr = string[] | number[]


type StrArrOrNumArr2 = ToArray<string> | ToArray<number>;

Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets.

通常,分配性是期望的行为。为了避免这种行为,您可以用方括号将 extends 关键字的每一侧括起来。

type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
 
// 'StrArrOrNumArr' is no longer a union.
type StrArrOrNumArr = ToArrayNonDist<string | number>;
// type StrArrOrNumArr = (string | number)[]

leetcode TypeScript 面试题

https://www.cnblogs.com/xgqfrms/p/13611172.html

// import { expect } from "chai";

interface Action<T> {
  payload?: T;
  type: string;
}

class EffectModule {
  count = 1;
  message = "hello!";
  delay(input: Promise<number>) {
    return input.then((i) => ({
      payload: `hello ${i}!`,
      type: "delay"
    }));
  }
  setMessage(action: Action<Date>) {
    return {
      payload: action.payload!.getMilliseconds(),
      type: "set-message"
    };
  }
}

// 修改 Connect 的类型,让 connected 的类型变成预期的类型
// type Connect = (module: EffectModule) => any;

// 1. 过滤出成员属性的函数 ✅
type FilterFunction<Obj> = {
  // 仅仅过滤 Function
  [K in keyof Obj]: Obj[K] extends Function ? K : never;
}[keyof Obj];

type Connect = (
  module: EffectModule
) => {
  // 2. 使用 infer 动态获取 参数类型
  [K in FilterFunction<EffectModule>]: EffectModule[K] extends (
    input: Promise<infer T>
  ) => Promise<Action<infer U>>
    ? (input: T) => Action<U>
    : EffectModule[K] extends (action: Action<infer T>) => Action<infer U>
    ? (action: T) => Action<U>
    : never;
  // 👎 input & action 形参必须写死 ???
};

/*
type Connect = (module: EffectModule) => {
  delay: (input: number) => Action<string>;
  setMessage: (action: Date) => Action<number>;
}
*/

const connect: Connect = (module) => ({
  delay: (input: number) => ({
    type: "delay",
    payload: `hello 2`
  }),
  setMessage: (input: Date) => ({
    type: "set-message",
    payload: input.getMilliseconds()
  })
});

type Connected = {
  delay(input: number): Action<string>;
  setMessage(action: Date): Action<number>;
};

export const test = connect(new EffectModule());
/*
type Connect = (module: EffectModule) => {
  delay: (input: number) => Action<string>;
  setMessage: (action: Date) => Action<number>;
}
*/

export const connected: Connected = connect(new EffectModule());

refs

https://www.typescriptlang.org/docs/handbook/2/types-from-types.html

https://www.typescriptlang.org/docs/handbook/2/conditional-types.html

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html



©xgqfrms 2012-2020

www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2022-05-05 21:06  xgqfrms  阅读(27)  评论(2编辑  收藏  举报