# 内置高级类型
# Parameters
代码语言:javascript复制type Parameters<T extends (...args: any) => any)
= T extends (...args: infer P) => any
? P
: never;
type ParametersResult = Parameters<(name: string, age: number) => void>;
// type ParametersResult = [name: string, age: number]
# ReturnType
代码语言:javascript复制type ReturnType<T extends (...args: any) => any)
= T extends (...args: any) => infer R
? R
: never;
type ReturnTypeResult = ReturnType<(name: string, age: number) => string>;
// type ReturnTypeResult = string
# ConstructorParameters
代码语言:javascript复制type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (
...args: infer P
) => any
? P
: never;
type ConstructorParametersResult = ConstructorParameters<new (name: string, age: number) => void>;
// type ConstructorParametersResult = [name: string, age: number]
# InstanceType
代码语言:javascript复制interface Person {
name: string;
age: number;
}
interface PersonConstructor {
new (name: string, age: number): Person;
}
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (
...args: any
) => infer R
? R
: any;
type InstanceTypeResult = InstanceType<PersonConstructor>;
// type InstanceTypeResult = Person
# ThisParameterType
代码语言:javascript复制type Person = {
name: "cell";
};
function say(this: Person) {
console.log(this.name);
}
type ThisParameterType<T> = T extends (this: infer P, ...args: any[]) => any ? P : unknown;
type ThisParameterTypeResult = ThisParameterType<typeof say>;
// type ThisParameterTypeResult = {
// name: "cell";
// }
# OmitThisParameter
代码语言:javascript复制type Person = {
name: "cell";
};
function say(this: Person, age: number) {
console.log(this.name);
return this.name "-" age;
}
type OmitThisParameter<T> = unknown extends ThisParameterType<T>
? T
: T extends (...args: infer A) => infer R
? (...args: A) => R
: T;
type sayType = typeof say;
// type sayType = (this: Person, age: number) => string
type OmitThisParameterResult = OmitThisParameter<sayType>;
//type OmitThisParameterResult = (age: number) => string
# Partial
代码语言:javascript复制type Partial<T> = {
[P in keyof T]?: T[P];
};
type PartialResult = Partial<{ name: string; age: number }>;
// type PartialResult = {
// name?: string | undefined;
// age?: number | undefined;
// }
# Required
代码语言:javascript复制type Required<T> = {
[P in keyof T]-?: T[P];
};
type RequiredResult = Required<{ name?: string; age?: number }>;
// type RequiredResult = {
// name: string;
// age: number;
// }
# Readonly
代码语言:javascript复制type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type ReadonlyResult = Readonly<{ name: string; age: number }>;
// type ReadonlyResult = {
// readonly name: string;
// readonly age: number;
// }
# Pick
映射类型的语法用于构造新的索引类型,在构造的过程中可以对索引和值做一些修改或过滤。
比如可以用 Pick
实现过滤:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type PickResult = Pick<{ name: string; age: number }, "name">;
// type PickResult = {
// name: string;
// }
# Record
Record
用于创建索引类型,传入 key
和 值
的类型:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type RecordResult = Record<"name" | "age", string>;
// type RecordResult = {
// name: string;
// age: string;
// }
注意,此处 keyof any
结果是 string | number | symbol
,所以 Record
的 key
可以是 string
、number
、symbol
。
# Exclude
从一个联合类型中去掉一部分类型时,可以用 Exclude
类型:
type Exclude<T, U> = T extends U ? never : T;
type ExcludeResult = Exclude<"a" | "b" | "c", "a" | "b">;
// type ExcludeResult = "c"
# Extract
Exclude
反过来就是 Extract
,也就是取交集:
type Extract<T, U> = T extends U ? T : never;
type ExtractResult = Extract<"a" | "b" | "c", "a" | "b">;
// type ExtractResult = "a" | "b"
# Omit
Omit
用于从一个类型中去掉某些属性:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type OmitResult = Omit<{ name: string; age: number }, "name">;
// type OmitResult = {
// age: number;
// }
# Awaited
取 Promise
的 ValuType
的高级类型:
type Awaited<T> = T extends null | undefined
? T
: T extends object & { then(onfulfilled: infer F): any }
? F extends (value: infer V, ...args: any) => any
? Awaited<V>
: never
: T;
type AwaitedResult = Awaited<Promise<string>>;
// type AwaitedResult = string
# NonNullable
NonNullable
用于判断是否为非空类型,也就是不是 null
或者 undefined
的类型的:
type NonNullable<T> = T extends null | undefined ? never : T;
type NonNullableResult = NonNullable<string | null | undefined>;
// type NonNullableResult = string
type NonNullableResult2 = NonNullable<string | number>;
// type NonNullableResult2 = string | number
# Uppercase, Lowercase, Capitalize, Uncapitalize
代码语言:javascript复制type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;
intrinsic
是固有的意思,就像 js 里面的有的方法打印会显示 [native code]
一样。这部分类型不是在 ts 里实现的,而是编译过程中由 js 实现的。
# 类型编程实战
类型编程可以动态生成类型,对已有类型做修改。
类型编程的意义:需要动态生成类型的场景,必然要用类型编程做一些运算。有的场景下可以不用类型编程,但是用了能够有更精准的类型提示和检查。
# ParseQueryString
JavaScript 实现:
代码语言:javascript复制function parseQueryString(queryStr) {
if (!queryStr || !queryStr.length) return {};
const queryObj = {};
const queryArr = queryStr.split("&");
for (let i = 0; i < queryArr.length; i ) {
const [key, value] = queryArr[i].split("=");
if (queryObj[key]) {
if (Array.isArray(queryObj[key])) {
queryObj[key].push(value);
} else {
queryObj[key] = [queryObj[key], value];
}
} else {
queryObj[key] = value;
}
}
return queryObj;
}
const result = parseQueryString("a=1&a=2&b=3");
// {
// "a": [
// "1",
// "2"
// ],
// "b": "3"
// }
类型推导:
代码语言:javascript复制type ParseParam<Param extends string> = Param extends `${infer K}=${infer V}`
? { [key in K]: V }
: Record<string, any>;
type MergeValues<One, Other> = One extends Other
? One
: Other extends unknown[]
? [One, ...Other]
: [One, Other];
type MergeParams<OneParam extends Record<string, any>, OtherParam extends Record<string, any>> = {
readonly [K in keyof OneParam | keyof OtherParam]: K extends keyof OneParam
? K extends keyof OtherParam
? MergeValues<OneParam[K], OtherParam[K]>
: OneParam[K]
: K extends keyof OtherParam
? OtherParam[K]
: never;
};
type ParseQueryString<Str extends string> = Str extends `${infer First}&${infer Rest}`
? MergeParams<ParseParam<First>, ParseQueryString<Rest>>
: ParseParam<Str>;
function parseQueryString<Str extends string>(queryStr: Str): ParseQueryString<Str>;
function parseQueryString(queryStr: string) {
if (!queryStr || !queryStr.length) return {};
const queryObj: Record<string, any> = {};
const queryArr = queryStr.split("&");
for (let i = 0; i < queryArr.length; i ) {
const [key, value] = queryArr[i].split("=");
if (queryObj[key]) {
if (Array.isArray(queryObj[key])) {
queryObj[key].push(value);
} else {
queryObj[key] = [queryObj[key], value];
}
} else {
queryObj[key] = value;
}
}
return queryObj;
}
const result = parseQueryString("a=1&a=2&b=3");
# KebabCaseToCamelCase
代码语言:javascript复制type KebabCaseToCamelCase<Str extends string> = Str extends `${infer Cursor}-${infer Rest}`
? `${Cursor}${KebabCaseToCamelCase<Capitalize<Rest>>}`
: Str;
type Result = KebabCaseToCamelCase<"hello-world">;
// type Result = "helloWorld"
# CamelCaseToKebabCase
代码语言:javascript复制type CamelCaseToKebabCase<Str extends string> = Str extends `${infer Cursor}${infer Rest}`
? Cursor extends Lowercase<Cursor>
? `${Cursor}${CamelCaseToKebabCase<Rest>}`
: `-${Lowercase<Cursor>}${CamelCaseToKebabCase<Rest>}`
: Str;
type Result = CamelCaseToKebabCase<"helloWorld">;
// type Result = "hello-world"
# Chunk
TypeScript 实现:
代码语言:javascript复制function Chunk(
Arr: Array<number>,
ItemLen: number,
CurItem: Array<number>,
Result: Array<Array<number>>
): any {
const [cursor, ...Rest] = Arr;
if (cursor) {
if (CurItem.length === ItemLen) {
return Chunk(Rest, ItemLen, [cursor], [...Result, CurItem]);
} else {
return Chunk(Rest, ItemLen, [...CurItem, cursor], Result);
}
} else {
return [...Result, CurItem];
}
}
const res = Chunk([1, 2, 3, 4, 5, 6, 7, 8], 3, [], []);
// const res = [
// [1, 2, 3],
// [4, 5, 6],
// [7, 8]
// ]
类型编程:
代码语言:javascript复制type Chunk<
Arr extends unknown[],
ItemLen extends number,
CurItem extends unknown[] = [],
Result extends unknown[][] = []
> = Arr extends [infer Cursor, ...infer Rest]
? CurItem["length"] extends ItemLen
? Chunk<Rest, ItemLen, [Cursor], [...Result, CurItem]>
: Chunk<Rest, ItemLen, [...CurItem, Cursor], Result>
: [...Result, CurItem];
type Result = Chunk<[1, 2, 3, 4, 5, 6, 7, 8], 3>;
// type Result = [
// [1, 2, 3],
// [4, 5, 6],
// [7, 8]
// ]
# TupleToNestedObject
代码语言:javascript复制type TupleToNestedObject<Tuple extends unknown[], Value> = Tuple extends [
infer Cursor,
...infer Rest
]
? {
[Key in Cursor as Key extends keyof any ? Key : never]: Rest extends unknown[]
? TupleToNestedObject<Rest, Value>
: Value;
}
: Value;
type Result = TupleToNestedObject<["a", "b", "c"], 1>;
// type Result = {
// a: {
// b: {
// c: 1
// }
// }
// }
# PartialObjectPropByKeys
代码语言:javascript复制interface Person {
name: string;
age: number;
phone: string;
}
type Copy<Obj extends Record<string, any>> = {
[K in keyof Obj]: Obj[K];
};
type PartialObjectPropByKeys<T extends Record<string, any>, Keys extends keyof T> = Copy<
Partial<Pick<T, Extract<keyof T, Keys>>> & Omit<T, Keys>
>;
type Result = PartialObjectPropByKeys<Person, "name" | "age">;
// type Result = {
// name?: string | undefined;
// age?: number | undefined;
// phone: string;
// }
# 函数重载
ts 支持函数重载,也就是同名的函数可以有多种类型定义:
重载的写法一共有三种:
代码语言:javascript复制// 同名函数重载
declare function func(age: number): number;
declare function func(age: string): string;
// 有函数实现可以不用带 declare
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
return x y;
}
// 用 interface 声明函数 和 函数重载
interface Func1 {
(age: number): number;
(age: string): string;
}
declare const func1: Func1;
// 使用交叉类型实现函数重载
type Func2 = ((name: string) => string) & ((name: number) => number);
declare const func2: Func2;
# UnionToTuple
取重载函数的 ReturnType
返回的是最后一个重载的返回值类型:
declare function func(age: number): number;
declare function func(age: string): string;
type Result = ReturnType<typeof func>;
// type Result = string
interface Func1 {
(age: number): number;
(age: string): string;
}
type Result1 = ReturnType<Func1>;
// type Result1 = string
type Func2 = ((name: string) => string) & ((name: number) => number);
type Result2 = ReturnType<Func2>;
// type Result2 = number
重载函数能通过函数交叉的方式写,并且也能实现联合转交叉,所以就能拿到联合类型的最后一个类型:
代码语言:javascript复制type UnionToIntersection<U> = (U extends U ? (x: U) => unknown : never) extends (
x: infer I
) => unknown
? I
: never;
type UnionToFuncIntersection<T> = UnionToIntersection<T extends any ? () => T : never>;
type UnionToTuple<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R
? [...UnionToTuple<Exclude<T, R>>, R]
: [];
type Result = UnionToTuple<1 | 2 | 3>;
// type Result = [1, 2, 3]
# join
代码语言:javascript复制declare function join<Delimiter extends string>(
delimiter: Delimiter
): <Items extends string[]>(...parts: Items) => JoinType<Items, Delimiter>;
type RemoveFirstDelimiter<Str extends string> = Str extends `${infer _}${infer Rest}` ? Rest : Str;
type JoinType<
Items extends any[],
Delimiter extends string,
Result extends string = ""
> = Items extends [infer Cur, ...infer Rest]
? JoinType<Rest, Delimiter, `${Result}${Delimiter}${Cur & string}`>
: RemoveFirstDelimiter<Result>;
let res = join("-")("cell", "in", "lab");
// let res: "cell-in-lab"
# DeepCamelize
代码语言:javascript复制type CamelizeArr<Arr> = Arr extends [infer First, ...infer Rest]
? [DeepCamelize<First>, ...CamelizeArr<Rest>]
: [];
type DeepCamelize<Obj extends Record<string, any>> = Obj extends unknown[]
? CamelizeArr<Obj>
: {
[Key in keyof Obj as Key extends `${infer First}_${infer Rest}`
? `${First}${Capitalize<Rest>}`
: Key]: DeepCamelize<Obj[Key]>;
};
type Result = DeepCamelize<{
a_b: {
c_d: {
e_f: 1;
};
};
g_h: [1, 2, 3];
}>;
// type Result = {
// aB: {
// cD: {
// eF: 1;
// };
// };
// gH: [1, 2, 3];
// }
# AllKeyPath
代码语言:javascript复制type AllKeyPath<Obj extends Record<string, any>> = {
[Key in keyof Obj]: Key extends string
? Obj[Key] extends Record<string, any>
? Key | `${Key}.${AllKeyPath<Obj[Key]>}`
: Key
: never;
}[keyof Obj];
type Result = AllKeyPath<{
a: {
b: {
c: 1;
};
};
d: 2;
}>;
// type Result = "a" | "a.b" | "a.b.c" | "d"
# Defaultize
代码语言:javascript复制type Defaultize<A, B> = Pick<A, Exclude<keyof A, keyof B>> &
Partial<Pick<A, Extract<keyof A, keyof B>>> &
Partial<Pick<B, Exclude<keyof B, keyof A>>>;
type Copy<Obj extends Record<string, any>> = {
[Key in keyof Obj]: Obj[Key];
};
type PreResult = Defaultize<{ a: 1; b: 2 }, { a: 1; c: 3 }>;
type Result = Copy<PreResult>;
// type Result = {
// a: 1;
// b?: 2 | undefined;
// c?: 3 | undefined;
// }