Skip to content

Instantly share code, notes, and snippets.

@hjzheng
Last active August 1, 2019 11:55
Show Gist options
  • Save hjzheng/d96c4aa8cd02a20d4a4feebb2815a4b1 to your computer and use it in GitHub Desktop.
Save hjzheng/d96c4aa8cd02a20d4a4feebb2815a4b1 to your computer and use it in GitHub Desktop.
typescript

泛型的好处

  1. 函数和类可以支持多种类型,增加的程序的可扩展性
  2. 不必写多条函数重载,联合类型声明,增强代码的可读性
  3. 灵活控制类型之间的约束

总结: 泛型不仅可以保持类型的一致性,又不失程序的灵活性,同时也可以通过泛型约束,控制类型之间的约束。从代码的上来看,可读性,简洁性,远优于函数重载,联合类型声明以及 any 类型的声明。

类型检查机制:

TypeScript 编译器在做类型检查的时,所秉承的一些原则,以及表现出的一些行为

作用: 辅助开发,提高开发效率

  1. 类型推断
  2. 类型兼容性
  3. 类型保护

类型推断

不需要指定变量的类型(函数的返回值类型),Typescript 可以根据某些规则自动为其推断一个类型。

  • 基础类型推断
  • 最佳通用类型推断
  • 上下文类型推断
  1. 前两者都是从右往左的推断,根据值去推断, 例如 let a = 1 // a 被推断的类型是 number
  2. 最佳通用类型推断 例如 let b = [1, 'a'] // b 的最佳通用类型 (string | number)[]
  3. 根据上下类型推断 例如 window.onkeydown = (event) => {} // 推断 event 为 KeyboardEvent

当类型推断不符合你的要求的时候,你可以使用类型断言 as,但是类型断言不能乱用,要对自己上下文充分了解

类型兼容性

当一个类型Y可以被赋值给另一个类型X时候,我们就可以说类型X兼容类型Y

// 接口兼容性
interface X {
   a: any,
   b: any
}

interface Y {
   a: any,
   b: any,
   c: any
}

let x: X = {a:1, b:2}
let y: Y = {a:1, b:2, c: 4}

x = y
y = x // 不兼容 x 缺少 c

// 成员少的兼容成员多的

函数的兼容性

  • 参数个数
  1. 参数多的的兼容参数少的
  2. 可选参数和剩余参数
  • 参数类型 - 对象参数类型

  • 返回类型

枚举兼容性

  • 枚举之间是不兼容的,但是枚举成员可以兼容数字和字符串

类的兼容性

  • 静态类型和构造函数不参与兼容比较,比较的是实例,如果有私有成员,就不兼容了,除非存在继承关系

泛型的兼容性

类型保护机制

高级类型

交叉类型和联合类型

// 交叉类型
interface DogInterface {
  run():void
}

interface CatInterface {
 jump():void
}

let pet: DogInterface & CatInterface = {
 run(){},
 jump(){}
}

// 联合类型
let a: number | string = 'a'

// 字面量联合类型 (只能是字面量中的一种)
let b: 'a' | 'b' | 'c'

// 对象的联合类型
interface Square {
   kind: "square";
   size: number;
}
interface Rectangle {
   kind: "rectangle";
   width: number;
   height: number;
}
interface Circle {
   kind: "circle";
   radius: number;
}
type Shape = Square | Rectangle | Circle
function area(s: Shape) {
   switch (s.kind) {
       case "square":
           return s.size * s.size;
       case "rectangle":
           return s.height * s.width;
       case 'circle':
           return Math.PI * s.radius ** 2
       default:
           return ((e: never) => {throw new Error(e)})(s)
   }
}
console.log(area({kind: 'circle', radius: 1}))

 

索引类型

let obj = {
    a: 1,
    b: 2,
    c: 3
}

// function getValues(obj: any, keys: string[]) {
//     return keys.map(key => obj[key])
// }
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
    return keys.map(key => obj[key])
}
console.log(getValues(obj, ['a', 'b']))
// console.log(getValues(obj, ['d', 'e']))

// keyof T
interface Obj {
    a: number;
    b: string;
}
let key: keyof Obj

// T[K]
let value: Obj['a']

// T extends U

映射类型 (其实是预定义好的一些泛型别名)

将一个旧的类型生成一个新的类型

interface Obj {
   a: string
   b: string
   c: string
}


// 映射类型 同态
type ReadonlyObj = Readonly<Obj>;

type PartialObj = Partial<Obj>;

type PickObj = Pick<Obj, 'a' | 'b'>

// 映射类型 非同态
type RecordObj = Record<'x'|'y', Obj>

条件类型

// T extends U ? X : Y

type TypeName<T> =
    T extends string ? "string" :
    T extends number ? "number" :
    T extends boolean ? "boolean" :
    T extends undefined ? "undefined" :
    T extends Function ? "function" :
    "object";
type T1 = TypeName<string>
type T2 = TypeName<string[]>

// (A | B) extends U ? X : Y
// (A extends U ? X : Y) | (B extends U ? X : Y)
type T3 = TypeName<string | string[]>

type Diff<T, U> = T extends U ? never : T
type T4 = Diff<"a" | "b" | "c", "a" | "e">
// Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e">
// never | "b" | "c"
// "b" | "c"

type NotNull<T> = Diff<T, null | undefined>
type T5 = NotNull<string | number | undefined | null>

// Exclude<T, U>
// NonNullable<T>

// Extract<T, U>
type T6 = Extract<"a" | "b" | "c", "a" | "e">

// ReturnType<T>
type T8 = ReturnType<() => string>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment