深入

类型系统

Typescript 并不总是可靠的,如下:

const temp: string = (1 as unknown) as string

typeof temp === 'number'

类型层次:never < null < undefined < string | booolean | ... < unkonwn < any

逆变

子类型可以赋值给父类型,叫做协变 (covariant)

interface Person {
    name: string;
    age: number;
} 

interface Guang {
    name: string;
    age: number;
    hobbies: string[]
}

let person: Person = {
    name: '',
    age: 20
};
let guang: Guang = {
    name: 'guang',
    age: 20,
    hobbies: ['play game', 'writing']
};

person = guang;

协变

父类型可以赋值给子类型,叫做逆变 (contravariant)。 函数的参数有逆变的性质 (而返回值是协变的,也就是子类型可以赋值给父类型

interface Person {
    name: string;
    age: number;
} 

interface Guang {
    name: string;
    age: number;
    hobbies: string[]
}

let printHobbies: (guang: Guang) => void;

printHobbies = (guang) => {
    console.log(guang.hobbies);
}

let printName: (person: Person) => void;

printName = (person) => {
    console.log(person.name);
}

// ok
printHobbies = printName;

// invalid
printName = printHobbies;

参数的位置是逆变的,返回值逆变

// return type invalid, because boolean is not sub type of void
type Func = (name: string) => void

let test:Func = (name:any) => boolean

// parmas type valid, because any is parent type of string && undefined is sub type of void
type Func = (name: string) => void

let test:Func = (name:any) => undefined

声明文件

  • 内部自带声明文件:lib.d.tslib.dom.d.ts
  • 第三方声明文件:JqueryDefinitelyTyped在新窗口打开 查找第三方声明文件
  • 自定义声明文件

tsconfig.json

  • baseUrl
  • paths
  • rootDirs
  • traceResolution,追踪编译过程日志

tsc 编译

tsc 没有做 polyfill 的处理,需要全量引入 core-js,babel 的 @babel/preset-env 会根据 targets 的配置按需引入 core-js

  • --watch
  • --build
  • --module
  • --target
  • --project
  • --help

tsc init 初始化 tsconfig.json

babel 编译

优点

  • babel 编译 ts 代码的优点是可以通过插件支持更多的语言特性,而且生成的代码是按照 targets 的配置按需引入 core-js
  • 编译 ts 代码来生成体积更小的代码,不做类型检查编译速度也更快
  • 如果想做类型检查可以单独执行 tsc --noEmit

babel 不足

  • const enum (会作为 enum 处理)
  • namespace 的跨文件合并,因为是每个文件单独编译的 (tsc 是整个项目一起编译)
  • 导出非 const 的值
  • 过时的 export = import = 的模块语法
  • 不做类型检查,也不会生成 d.ts 文件

三斜线指令

必须放在代码文件首行,否则无效

/// <reference path=''>
/// <reference lib=''>

体操

StartWith

// 判断字符串是否以某个前缀开头,也是通过模式匹配
type StartWith<Str extends string, Prefix extends string> = Str extends `${Prefix}${string}` ? true : false

type Start = StartWith<'abc', 'a'>  // true
type Start = StartWith<'bbc', 'a'>  // false

Replace

字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型

type Replace<Str extends string, From extends string, To extends string> = Str extends `${infer Prefix}${ From }${ infer Suffix }` ? `${ Prefix } ${ To } ${ Suffix } `: Str

type R = Replace<'abc is ?', '?', 'ok'>

// type R = "abc is  ok  "

Trim

type TrimRightStr<Str extends string> = Str extends `${infer Rest}${' ' | '\n' | '\r'}` ? TrimRightStr<Rest>: Str
type TrimLefttStr<Str extends string> = Str extends `${' ' | '\n' | '\r'}${infer Rest}` ? TrimLefttStr<Rest>: Str

type Trim<Str extends string> = TrimRightStr<TrimLefttStr<Str>>

type OKTrim = Trim<'   000   '>
// '000'

Uppercase

type UppercaseKey<O extends object> = {
    [Key in keyof O as Uppercase<Key & string>] : O[Key]
}


type obj = {
  readonly name: string;
  age?: number;
  gender: boolean;
}

type T = UppercaseKey<obj>

// output
// type T = {
//     readonly NAME: string;
//     AGE?: number | undefined;
//     GENDER: boolean;
// }

Record

type Keys = 'name' | 'age'

type Test = Record<Keys, number>

// type Test = {
//     name: number;
//     age: number;
// }

type Record<K extends string | number | symbol, T> = { [P in K]: T; }

Filter


type FilterType<O extends Record<string, any>, T> = {
    [Key in keyof O as T extends O[Key] ? Key : never] : O[Key]
}

type StringType = FilterType<{
    name: string,
    age: number,
    oters: number[]
}, number[]>

// type StringType = {
//     oters: number[];
// }

Deep

type DeepPromise<T> = T extends Promise<infer P> ? DeepPromise<P> : T

type SS = DeepPromise<Promise<Promise<Promise<string>>>>

// number

Reverse

type ReverseArray<Arr extends unknown> = Arr extends [infer First, ...infer Rest] ? [...ReverseArray<Rest>, First] : Arr

type SS = ReverseArray<[1,3,6,'0']>

// ['0',6,,3,1]

Include

type Euqal<A, B> = (A extends B ? true : false) & (B extends A ? true : false)

type Include<Arr extends unknown[], Item> = Arr extends [infer Frist, ...infer Rest] ? Euqal<Frist, Item> extends true ? true : Include<Rest, Item> :false

type Arr = Include<[1,2,3], 1>  // true
type Arr1= Include<[1,2,3], 11> // false

Removeitem

type Euqal<A, B> = (A extends B ? true : false) & (B extends A ? true : false)

type RemoveItem<Arr extends unknown[], Item, Results extends unknown[] = []> = Arr extends [infer First, ...infer Rest] ? Euqal<First, Item> extends true ? RemoveItem<Rest,Item, Results> : RemoveItem<Rest,Item, [...Results,First]> : Results

type Arr = RemoveItem<[1,2,3], 1>   // [2,3]

ReplaceAll

type ReplaceAll<
    Str extends string, 
    From extends string, 
    To extends string
> = Str extends `${infer Left}${From}${infer Right}`
        ? `${Left}${To}${ReplaceAll<Right, From, To>}`
        : Str;

String Union

type StringUnion<Str extends string> = Str extends `${infer First}${infer Rest}` ? First | StringUnion<Rest> : never

type Str = StringUnion<'smile'>
// 's' 'm' 'i' 'l' 'e'

String Reverse

type StrReverse<Str extends string, Results extends string = ''> = Str extends `${infer First}${infer Rest}` ? StrReverse<Rest, `${First}${Results}`> : Results

type Str = StrReverse<'smile'>
// 'elims'

Array Add

type BuildNewArray<Length extends number, Ele = unknown, Arr extends unknown[] = []> = Arr['length'] extends Length ? Arr : BuildNewArray<Length, Ele,[...Arr, Ele]>

type Add<Num1 extends number, Num2 extends number> = 
    [...BuildNewArray<Num1>,...BuildNewArray<Num2>]['length'];


type AddResult = Add<32, 25>;

Isany

type isAny<T> = 'any' extends ('ok' & T) ? true : false

type test = isAny<any>

isUnion

type isUnion<A, B=A> = A extends B ? [B] extends [A] ? false : true : never

type test = isUnion<'a' | 'b'>
type test1 = isUnion<'a'>

isNever

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

type ok1 = isNever<'never'>

Index type

type IndexType =  keyof any

// string | number | symbol
上次更新:
贡献者: Joe