представлять
infer
впервые появился здесьPRв, значит вextends
Переменная типа, которая будет выводиться в условном выражении.
Простой пример выглядит следующим образом:
type ParamType<T> = T extends (param: infer P) => any ? P : T;
в этом условном выраженииT extends (param: infer P) => any ? P : T
середина,infer P
Представляет параметр функции, который необходимо вывести.
Все предложение выражается как: еслиT
может быть назначен на(param: infer P) => any
, то результат(param: infer P) => any
параметры типаP
, иначе он возвращается какT
.
interface User {
name: string;
age: number;
}
type Func = (user: User) => void
type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string
встроенный тип
В версии 2.8 TypeScript имеет встроенныйinfer
Соответствующие типы отображения:
-
Тип возвращаемого значения, используемый для извлечения типа функции:
type ReturnType<T> = T extends (...args: any[]) => infer P ? P : any;
По сравнению с примером, приведенным в начале статьи,
ReturnType<T>
просто поставьinfer P
Переместитесь из позиции параметра в позицию возвращаемого значения, так что на этот разP
То есть он представляет тип возвращаемого значения, который необходимо вывести.type Func = () => User; type Test = ReturnType<Func>; // Test = User
-
Используется для извлечения типа параметра (экземпляра) в конструкторе:
Конструктор может использовать
new
для создания экземпляра, поэтому его тип обычно представляется следующим образом:type Constructor = new (...args: any[]) => any;
когда
infer
Используется в типах конструктора, может использоваться в позиции параметраnew (...args: infer P) => any;
и местоположение возвращаемого значенияnew (...args: any[]) => infer P;
.Таким образом, встроены следующие два типа сопоставления:
// 获取参数类型 type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never; // 获取实例类型 type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any; class TestClass { constructor( public name: string, public string: number ) {} } type Params = ConstructorParameters<typeof TestClass>; // [string, numbder] type Instance = InstanceType<typeof TestClass>; // TestClass
некоторые варианты использования
До сих пор, я полагаю, у вас естьinfer
Теперь, когда у нас есть базовое понимание, давайте взглянем на некоторые «дерзкие операции», которые его используют:
-
tupleПеременаunion,Такие как:
[string, number]
->string | number
Прежде чем ответить, нужно понять, что тип кортежа может быть присвоен типу массива при определенных условиях:
type TTuple = [string, number]; type TArray = Array<string | number>; type Res = TTuple extends TArray ? true : false; // true type ResO = TArray extends TTuple ? true : false; // false
Поэтому в сотрудничестве с
infer
, это легко сделать:type ElementOf<T> = T extends Array<infer E> ? E : never type TTuple = [string, number]; type ToUnion = ElementOf<TTuple>; // string | number
существуетstackoverflowСм. другое решение выше, относительно простое (крупный рогатый скот) и одиночное (принудительное):
type TTuple = [string, number]; type Res = TTuple[number]; // string | number
-
unionПеременаintersection,Такие как:
string | number
->string & number
Это может быть немного более хлопотно, нужно
infer
Сотрудничать"Distributive conditional types"использовать.существуетСсылки по теме, мы можем узнать, что «дистрибутивные условные типы» — это условные типы, состоящие из «параметра голого типа». А "параметр голого типа" означает, что он не был
Wrapped
тип (например:Array<T>
,[T]
,Promise<T>
и т. д. не являются «параметром голого типа»). «Распределительные условные типы» в основном используются для разделенияextends
Тип объединения левой части, например: в условном типеT extends U ? X : Y
в, когдаT
даA | B
, он будет разделен наA extends U ? X : Y | B extends U ? X : Y
;С этой предпосылкой повторно используйте его в положении инвертора,Несколько типов-кандидатов одной и той же переменной типа будут выведены как перекрестные типы.характеристики, то есть
type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never; type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number
Следовательно, совмещая вышеперечисленные пункты, мы можем получитьstackoverflowОтвет выше:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; type Result = UnionToIntersection<string | number>; // string & number
при входящем
string | number
Время:-
первый шаг:
(U extends any ? (k: U) => void : never)
расколет союз на(string extends any ? (k: string) => void : never) | (number extends any ? (k: number)=> void : never)
, то есть получить(k: string) => void | (k: number) => void
; -
Шаг 2:
(k: string) => void | (k: number) => void extends ((k: infer I)) => void ? I : never
, исходя из вышеизложенного, можно сделать вывод, чтоI
дляstring & number
.
-
Конечно, вы можете сыграть и другие трюки, напримерunionПеременаtuple.
Вопрос интервью TypeScript от LeetCode
некоторое время назад, вGitHubЯ нашел интересный вопрос интервью от LeetCode TypeScript, Общий смысл вопроса:
Предположим, что есть такой тип (класс, указанный в исходном вопросе, здесь упрощен до интерфейса):
interface Module {
count: number;
message: string;
asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
syncMethod<T, U>(action: Action<T>): Action<U>;
}
проходя черезConnect
После функции тип возвращаемого значения
type Result {
asyncMethod<T, U>(input: T): Action<U>;
syncMethod<T, U>(action: T): Action<U>;
}
вAction<T>
определяется как:
interface Action<T> {
payload?: T
type: string
}
Вот два основных момента
- выбрать функцию
- Тип состояния + упомянутый в этой статье
infer
метод выбора функций, уже вhandbookЭто было дано в , просто оцените, что значение может быть присвоено функции:
type FuncName<T> = {
[P in keyof T]: T[P] extends Function ? P : never;
}[keyof T];
type Connect = (module: Module) => { [T in FuncName<Module>]: Module[T] }
/*
* type Connect = (module: Module) => {
* asyncMethod: <T, U>(input: Promise<T>) => Promise<Action<U>>;
* syncMethod: <T, U>(action: Action<T>) => Action<U>;
* }
*/
Следующий шаг относительно прост, в основном используется тип условия +infer
, если функция может быть назначенаasyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>
, то значение равноasyncMethod<T, U>(input: T): Action<U>
. Конкретного ответа не дано, и заинтересованные друзья могут попробовать.
Более
- Глубокое понимание TypeScript
- Умелое использование TypeScript (4)
- Используйте TypeScript умело (3)
- Использование TypeScript (б)
- Умело используйте TypeScript (1)
Ссылаться на
Для получения дополнительных статей, пожалуйста, обратите внимание на наш публичный аккаунт: