Open TinyScript opened 2 years ago
Exclude<"a" | "b" | "c", "a"> // => "b" | "c";
// 实现,如果T被U约束,返回never,否则为T
type MyExclude<T, U> = T extends U ? never : T;;
MyExclude<"a" | "b" | "c", "a"> // => "b" | "c";
type X = Promise<string>;
type Y = Promise<{ field: number }>;
type Z = Promise<string | number>;
// 实现1,易读版本
type Helper<P> = P extends Promise<any> ? Helper<MyAwaited<P>> : P;
type MyAwaited<P> = P extends Promise<infer V> ? Helper<V> : P;
// 实现2,正式版本,递归
type MyAwaited<P> = P extends Promise<infer V> ? MyAwaited<V> : P;
MyAwaited<X>; // => string
MyAwaited<Y>; // => { field: number }
MyAwaited<Z>; // => string | number
// 实现
type If<C, T, F> = C extends true ? T : F;
type A = If<true, 'a', 'b'> // => a
type B = If<false, 'a', 'b'> // => b
// 期望
Concat<[1, 2], [3, 4]> // => [1, 2, 3, 4]
// 实现
type Concat<T extends any[], U extends any[]> = T extends [... infer VT]
? U extends [...infer VU]
? [...VT, ...VU]
: never
: never
Concat<[], []> // => []
Concat<[], [1]> // => [1]
Concat<[1, 2], [3, 4]> // => [1, 2, 3, 4]
Concat<['1', 2, '3'], [false, boolean, '4']> // => ['1', 2, '3', false, boolean, '4']
// 期望
Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>; // => true
Includes<['Kars', 'Esidisi','Wamuu', 'Santana'], 'Dio'>; // =>false
// 实现
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer R]
? Equal<F, U> extends true
? true
: Includes<R, U>
: false
Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'> // => true
Includes<['Kars', 'Esidisi','Wamuu', 'Santana'], 'Dio'> // => false
Includes<[1, 2, 3, 5, 6, 7], 7> // => true
Includes<[1, 2, 3, 5, 6, 7], 4> // => false
Includes<[1, 2, 3], 2> // => true
Includes<[1, 2, 3], 1> // => true
Includes<[{}], { a: 'A' }> // => false
Includes<[boolean, 2, 3, 5, 6, 7], false> // => false
Includes<[true, 2, 3, 5, 6, 7], boolean> // => false
Includes<[false, 2, 3, 5, 6, 7], false> // => true
Includes<[{ a: 'A' }], { readonly a: 'A' }> // => false
Includes<[{ readonly a: 'A' }], { a: 'A' }> // => false
// 期望
Push<[1, 2], '3'> // => [1, 2, '3']
// 实现
type Push<T extends any[], U> = [...T, U]
Push<[], 1> // => [1]
Push<[1, 2], '3'> // => [1, 2, '3']
Push<['1', 2, '3'], boolean> // => ['1', 2, '3', boolean]
// 期望
Unshift<[1, 2], 0> // => [0, 1, 2]
// 实现
type Unshift<T extends any[], U> = [U, ...T]
const foo = (arg1: string, arg2: number, arg3: boolean): void => {} // => [string, number]
const bar = (arg1: boolean, arg2: {a: 'A'}): void => {} // => [boolean, {a: 'A'}]
const baz = (): void => {} // => []
// 实现
type MyParamters<T extends (...args: any[]) => any> = T extends (...args: [...infer P]) => any ? P : never;
MyParamters<typeof foo> // => [string, number]
MyParamters<typeof bar> // => [boolean, {a: 'A'}]
MyParamters<typeof baz> // => []
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
// 期望
ReturnType<typeof fn> // => "1 | 2"
// 实现
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type ComplexObject = {
a: [12, 'foo']
bar: 'hello'
prev(): number
}
const fn = (v: boolean) => v ? 1 : 2
const fn1 = (v: boolean, w: any) => v ? 1 : 2
MyReturnType<() => string> // => string
MyReturnType<() => 123> // => 123
MyReturnType<() => ComplexObject> // => ComplexObject
MyReturnType<() => Promise<boolean>> // => Promise<boolean>
MyReturnType<() => () => 'foo'> // => () => 'foo'
MyReturnType<typeof fn> // => 1 | 2
MyReturnType<typeof fn1> // => 1 | 2
interface Todo {
title: string
description: string
completed: boolean
}
// 期望
Omit<Todo, 'description'> // => { title: string, completed: boolean }
// 实现
type Exclude<T, K> = T extends K ? never : T;
type MyOmit<T, K> = { [k in Exclude<keyof T, K>]: T[k] }
MyOmit<Todo, 'description'> // => { title: string, completed: boolean }
MyOmit<Todo, 'description' | 'completed'> // => { title: string }
interface Todo1 {
title: string
description?: string
completed: boolean
}
interface Todo2 {
readonly title: string
description?: string
completed: boolean
}
interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}
// 期望
Readonly<Todo1, 'title' | 'description'>
/** => Expected
* {
* readonly title: string,
* readonly description?: string,
* completed: boolean
* }
*/
// 实现,思路:先选出需要readonly的key,再通过Omit过滤掉readonly的key,取并集
type Exclude<T, K> = T extends K ? never : T;
type Omit<T, K> = { [k in Exclude<keyof T, K>]: T[k] };
type MyReadonly<T, K extends keyof T = keyof T> = { readonly [k in K]: T[k] } & Omit<T, K>;
MyReadonly2<Todo1>; // => Readonly<Todo1>
MyReadonly2<Todo1, 'title' | 'description'>; // => Expected
MyReadonly2<Todo2, 'title' | 'description'>; // => Expected
// 预定义的数据:
type X = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
}
}
}
type Expected = {
readonly a: () => 22
readonly b: string
readonly c: {
readonly d: boolean
readonly e: {
readonly g: {
readonly h: {
readonly i: true
readonly j: 'string'
}
readonly k: 'hello'
}
}
}
}
// 期望
DeepReadonly<X> // => 与Expected相等
// 实现,除了函数不能被readonly,其他的类型全部递归readonly处理
type DeepReadonly<T> = { readonly [k in keyof T]: T[k] extends Function ? T[k] : DeepReadonly<T[k]> }
DeepReadonly<X> // => Expected
// 期望
TupleToUnion<[123, '456', true]> // => 123 | '456' | true
// 实现1,通过推断T数组类型拿到并集
type TupleToUnion<T> = T extends (infer R)[] ? R : never;
// 实现2,通过不断递归Rest的值,做到First | ...Rest的每次递归的并集累计效果
type TupleToUnion<T> = T extends [infer F, ...infer R] ? F | TupleToUnion<R> : never;
TupleToUnion<[123, '456', true]> // => 123 | '456' | true
TupleToUnion<[123]> // => 123
十八、Chainable Options 链式调用
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// 期望得到:
interface Result {
foo: number
name: string
bar: {
value: string
}
}
// 实现
type Chainable<T = {}> = {
option<K extends string, V>(key: K, value: V): Chainable<T & { [k in K]: V}>
get(): T
}
// 每次调用option都将key与value和T取并集
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
// 期望
type tail1 = Last<arr1> // => 'c'
type tail2 = Last<arr2> // => 1
// 实现
type Last<T extends any[]> = T extends [...infer R, infer L] ? L : never;
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
// 期望
type re1 = Pop<arr1> // => ['a', 'b', 'c']
type re2 = Pop<arr2> // => [3, 2]
// 实现
type Pop<T extends any[]> = T extends [...infer R, infer L] ? R : never;
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// 期望:
Promise.all([promise1, promise2, promise3] as const) // => Promise<[number, 42, string]>
/**
* 核心思路1:只要是[] as const,即为Readonly<[]>
* 核心思路2:Promise的泛型如果接收一个对象,对象key可作为数组的索引
* 例:
* Promise<['a', 'b', 'c']> => Promise.resolve(['a', 'b', 'c'])
* Promise<{ 0: 'a', 1: 'b', 2: 'c'}> => Promise.resolve(['a', 'b', 'c'])
*/
// 实现:
declare function PromiseAll<T extends unknow[]>(values: Readonly<[...T]>): Promise<
{
[k in keyof T]: T[k] extends Promise<infer V> ? V : T[k]
}
>
/**
* 简单解释:根据核心思路2,PromiseAll会返回一个Promise类型,泛型为数组类型,
* 并判断的值是否为Promise:
* - 若是true,拿到Promise的推断类型V,返回推断出来的V类型。
* - 若是false,说明为const,返回当前值
*/
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type Animal = Cat | Dog
// 期望:
LookUp<Animal, 'dog'> // => Dog
LookUp<Animal, 'cat'> // => Cat
// 实现
type LookUp<U, T> = U extends { type: infer V }
? V extends T
? U
: never
: never
// 期望
type trimed = TrimLeft<' Hello World '> // => "Hello World"
// 实现
type Ident = ' ' | '\t' | '\n'
type TrimLeft<T extends string> = T extends `${Ident}${infer R}` ? TrimLeft<R> : T;
一、Pick 选择属性
二、Readonly 只读
三、Tuple to Object 元组转对象
四、拿到数组第一个类型
五、获取元组长度