ts体操练习
原创大约 5 分钟
前言
typescript类型体操练习,用于学习ts。
题目
get-return-type
const fn = (v: boolean,a:string) => {
if (v)
return 1
else
return 2
}
// inter关键字,表示在extends条件语句中待推断的类型变量
type MyReturnType<T extends Function> = T extends (...arg: any) => infer p ? p :never
type a = MyReturnType<typeof fn>
Omit
interface Todo {
title: string
description: string
completed: boolean
}
// 利用as进行类型筛选
type MyOmit<T, R extends keyof T> = {
[Q in keyof T as Q extends R ? never : Q]: T[Q]
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false,
}
Pick
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false
}
// in 可以取联合类型的值, keyof可以获得联合类型
type MyPick<T, R extends keyof T> = {
[Q in R]: T[Q]
}
get-readonly-keys
interface Todo {
readonly title: string
readonly description: string
completed?: boolean
readonly abc: boolean
}
// Q: equal类型定义作用是什么?
// A: equal类型定义的作用是判断两个类型是否相等,如果相等返回true,否则返回false
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2 ) extends <T>() => T extends Y ? 1 : 2 ? true : false
// Q: {[K in keyof T]: T[K]}[keyof T] 和 keyof T 有什么区别?
// A: {[K in keyof T]: T[K]}[keyof T] 是T的所有属性的值的联合类型,keyof T 是T的所有属性的联合类型
type GetReadonlyKeys<T> = keyof {
[K in keyof T as Equal<
{ readonly [P in K]: T[P] },
{ [P in K]: T[P] }
> extends true
? K
: never]: T[K]
}
type Keys = GetReadonlyKeys<Todo> // expected to be "title" | "description"
simple-vue
type SimpleVueProps<D, M, C> = {
data: (this: void) => D
computed: C & ThisType<D>
methods: M & ThisType<D & GetComputed<C> & M>
}
// ThisType可以控制插入this
type GetComputed<C> = {
[K in keyof C]: C[K] extends (...args: any) => any ? ReturnType<C[K]> : never
}
declare function SimpleVue<D, M, C>(options: SimpleVueProps<D, M, C>): any
const instance = SimpleVue({
data() {
return {
firstname: 'Type',
lastname: 'Challenges',
amount: 10
}
},
computed: {
fullname() {
return this.firstname + ' ' + this.lastname
}
},
methods: {
hi() {
alert(this.fullname.toLowerCase())
}
}
})
ReadOnly
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
type MyReadonly<T> = {
readonly [K in keyof T]: T[K]
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
pick-to-readonly
interface Todo {
title: string
description: string
completed: boolean
}
// type MyPick<T, R extends keyof T> = {
// [Q in keyof T as Q extends R ? Q : never]: T[Q]
// }
// in 可以取联合类型的值, keyof可以获得联合类型
type MyPick<T, R extends keyof T> = {
[Q in R]: T[Q]
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false
}
const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: 'Hey',
description: 'foobar',
completed: false
}
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [R in K]: T[R]
} & {
[R in keyof T as R extends K ? never : R]: T[R]
}
todo.title = 'Hello' // Error: cannot reassign a readonly property
todo.description = 'barFoo' // Error: cannot reassign a readonly property
todo.completed = true // OK
deep-readonly
type X = {
x: {
a: 1
b: 'hi'
}
y: 'hey',
z: Date
}
type Expected = {
readonly x: {
readonly a: 1
readonly b: 'hi'
}
readonly y: 'hey'
readonly z: Date
}
type params = string | boolean | number | symbol | Function | Promise<any> | Date
type DeepReadonly<T> = T extends params ? T : { readonly [k in keyof T]: DeepReadonly<T[k]> }
type Todo = DeepReadonly<X> // should be same as `Expected`
tuple-to-union
type Arr = ['1', '2', '3']
type TupleToUnion<T> = T extends (infer U)[] ? U : never
type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
tuple-to-object
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type TupleToObject<T extends readonly (keyof any)[]> = {
[P in T[number]]: P
}
type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
chainable-option
declare const a: Chainable
// never是所有类型的子类型,never可以赋值给任何类型,但是没有类型可以赋值给never
type Chainable<T = {}> = {
option: <K extends string, V>(
key: K extends keyof T ? never : K,
value: V
) => K extends keyof T ? Chainable<Omit<T, K> & Record<K, V>>: Chainable<T & Record<K, V>>
get: () => T
}
const result1 = a
.option('foo', 123)
.option('bar', { value: 'Hello World' })
.option('name', 'type-challenges')
.get()
first
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type First<T extends any[]> = T["length"] extends 0 ? never : T[0]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
last
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
type Last<T extends any[]> = T extends [...infer rest, infer A] ? A : never
pop
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
type Pop<T extends any[]> = T extends [...infer rest, infer _] ? rest : []
type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]
length-of-tuple
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type Length<T extends readonly any[]> = T["length"]
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
exclude
// 联合类型的遍历,利用三元表达式,所有的类型都会遍历一遍
type MyExclude<T, U> = T extends U ? never : T
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
awaited
type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // string
// answer
type IsLikePromise<T> = {
then: (onfulfilled: (arg: T) => any) => any
}
type MyAwaited<T extends IsLikePromise<any>> = T extends IsLikePromise<infer R> ? R extends IsLikePromise<any> ? MyAwaited<R> : R : never
if
type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
// answer
type If<C extends boolean, T, F> = C extends true ? T : F
concat
type Result = Concat<[1], [2]> // expected to be [1, 2]
// answer
type Concat<T extends any[], U extends any[]> = [...T, ...U]
includes
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
// answer
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 L] ? Equal<F, U> extends true ? true : Includes<L, U> : false
push
type Result = Push<[1, 2], '3'> // [1, 2, '3']
// answer
type Push<T extends any[], U> = [...T, U]
unshift
type Result = Unshift<[1, 2], 0> // [0, 1, 2,]
// answer
type Unshift<T extends any[], U> = [U, ...T]
parameters
const foo = (arg1: string, arg2: number): void => {}
type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number]
// answer
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P)=> any ? P :never
promise-all
const promise1 = Promise.resolve(3);
const promise2: number = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// expected to be `Promise<[number, number, string]>`
// as const 可以固定类型,比如promise2不加: number,会被推断为42,而不是number。加了就是number
const p = PromiseAll([promise1, promise2, promise3] as const)
// answer
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{
[P in keyof T]: Awaited<T[P]>
}>