Skip to content

shockingsrose/type-challenges-answer

Repository files navigation

Type Challenges Answer

数据类型

Function Type

函数类型也属于对象类型

type Add = (a: number, b: number) => number
type IsFunctionEqualObject = Add extends Record<string, any> ? 1 : 2
// type IsFunctionEqualObject = 1

Generics 泛型

注意点:

变量需要定义在<>内部,参考00007-easy-readonly

/** 这里定义了K变量,并且给了默认值,使用时不需要传 */
type MyReadonly<T, K = keyof T> = {
  readonly [K in keyof T]: T[K]
}

可作为变量,通过递归实现

参考00012-medium-chainable-options.ts

Distributive Conditional Types 分布式条件

When conditional types act on a generic type, they become distributive when given a union type(the union type should be on the left of extends key)

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

type StrArrOrNumArr = ToArray<string | number>
// Output: type StrArrOrNumArr = string[] | number[]

Tuple 元组/数组

TypeScript 没有内置Tuple类型,但是可以自定义为type Tuple = readonly unknown[]

使用in遍历Tuple的值,作为 object 的 key,参考00011-easy-tuple-to-object.ts

type TupleToObject<T extends readonly any[]> = {
  [K in T[number]]: K
}

获取Tuple的长度, 00018-easy-tuple-length.ts

type Length<T extends readonly any[]> = T['length']

合并两个Tuple类型

type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]

keyof遍历数组, 参考00020-medium-promise-all

递归遍历数组,并返回数组类型,参考00459-medium-flatten

数组转联合类型

type A = ['1', '2']
type ArrayToUnion<T extends any[]> = T[number]

type B = ArrayToUnion<A>
// type B = '1' | '2'

String 字符串类型

模版字符串相关类型文档

在模版字符串中使用infer,处理字符串类型

type Space = ' ' | '\n' | '\t'
type Trim<S extends string> = S extends
  | `${Space}${infer R}`
  | `${infer R}${Space}`
  ? Trim<R>
  : S

用模版字符串拼接String类型

type MyCapitalize<S extends string> = S extends `${infer F}${infer R}`
  ? `${Uppercase<F>}${R}`
  : S

空字符串不等于${infer Head}${infer Tail}, 在这个模版字符串 Head会匹配第一个字符,Tail会匹配剩余所有字符

type B = '' extends `${infer Head}${infer Tail}` ? 1 : 2
// type B = 2

Uncapitalize会将给定字符串的第一个字符小写

将数字类型的 1.0 转换为字符串,会得到 "1"

type NumberToString<T extends number> = `${T}`

type A1 = NumberToString<1.1>;
// type A1 = "1.1"

type A2 = NumberToString<1.0>
// type A2 = "1"

never

如何判断never

type IsNever<T> = [T] extends [never] ? true : false

/** 这种情况下 A也是never */
type E = []
type A = IsNever<`__${E[number]}`>
// type A = true

关键字

keyof

操作 "对象类型" 生成key的联合类型,Link

type Arrayish = { [n: number]: unknown }
type A = keyof Arrayish // number

type Mapish = { [k: string]: boolean }
type M = keyof Mapish // number | string

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoKeys = keyof Todo // 'title' | 'description' | 'completed'

in

取联合类型的值,主要用于数组和对象的构建,不可直接用于 "对象类型"

type name = 'firstname' | 'lastname'
type TName = {
  [key in name]: string
}
// TName = { firstname: string, lastname: string }

extends

distributive

distributive

当 U 为对象时候,会在 Mapping Type 外部分布,当 U 为类似 string 或者 number 时候,会在 Mapping Type 内部分布),就能触发 Distributive Conditional Types

type A = {
  name: string
}

type B = {
  age: number
}

type Dis<T> = {
  [K in keyof T]: K
}

type res1 = Dis<A> | Dis<B>
type res2 = Dis<A | B>

type result = Equal<res1, res2>

infer

需跟在extends关键字后面

用于函数参数的例子

type MyParameters<T extends (...args: any[]) => any> = T extends (
  ...args: infer R
) => any
  ? R
  : []

用于数组的例子

type Push<T extends any[], U> = [...T, U]

用于字符串的例子

type TrimLeft<S extends string> = S extends `${Space}${infer R}`
  ? TrimLeft<R>
  : S

as

跟随在[K in keyof T]后面,例如[K in keyof T as K], 高级用法参考下面示例

// src/02852-medium-omitbytype.ts
type OmitByType<T, U> = NonNullable<{
  [K in keyof T as T[K] extends U ? never : K]: T[K]
}>
type ExtractToObject1<T extends Record<string, unknown>, U extends keyof T> = {
  // 左边的K会被`as`关键字改写 但是右边的K不会(始终为keyof T)
  [K in keyof T as K extends U ? keyof T[U] : K]: K
}

type test2 = {
  id: '1'
  prop1: { zoo: '2' }
  prop2: { foo: '4' }
}

type A = ExtractToObject1<test2, 'prop1'>
/* Output:

  type A = {
    id: 'id',
    zoo: 'prop1',
    prop2: 'prop2'
  }

*/

用法

递归类型推断

type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer R>
  ? R extends PromiseLike<any>
    ? MyAwaited<R>
    : R
  : T

数字运算

在 typescript 中,无法使用+, -, *, /等运算符,但是可以通过控制数组的长度来计算

type PlusOne<T extends any[]> = [0, ...T]['length']
type MinusOne<T extends any[]> = T extends [0, ...infer R] ? R['length'] : never
type MultiplyByTwo<T extends any[]> = [...T, ...T]['length']

type PlusOneResult = PlusOne<[1, 1, 1]>
// type PlusOneResult = 4

type MinusOneResult = MinusOne<[1, 1, 1]>
// type MinusOneResult = 2

type MultiplyByTwoResult = MultiplyByTwo<[1, 1, 1]>
// type MultiplyByTwoResult = 6

内置工具函数 Utility Types

Exclude

用法:Exclude<UnionType, ExcludedMembers> 用于排除联合类型里的部分成员

type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>
// type T1 = "c"

type T2 = Exclude<string | number | (() => void), Function>
// type T2 = string | number

type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; x: number }
  | { kind: 'triangle'; x: number; y: number }

type T3 = Exclude<Shape, { kind: 'circle' }>
type T3Result =
  | {
      kind: 'square'
      x: number
    }
  | {
      kind: 'triangle'
      x: number
      y: number
    }

About

use VSCode to do the type challenges, you can see https://github.com/type-challenges/type-challenges

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published