0%

Typescript-高级类型-1

交叉类型

多个类型合并为一个类型,包含所需的 所有类型 的特性

1
2
3
4
5
6
// T & U 交叉类型
const merge = <T, U>(arg1: T, arg2: U): T & U => {
let res = {} as T & U
res = Object.assign(arg1, arg2)
return res
}

联合类型

联合类型表示一个值可以是几种类型之一

1
2
3
4
5
6
7
8
9
10
const getLength = (content: string | number): number => {
if (typeof content === 'string') {
return content.length
} else {
return content.toString().length
}
}
console.log(getLength('123')); // 3
console.log(getLength(123)); // 3
console.log(getLength(true)); // error

类型保护

1
2
3
4
5
6
7
8
9
10
const valueList = ['123', 100]
const getRandomValue = () => {
const num = Math.random() * 10
if (num < 5) { return valueList[0] }
else { return valueList[1] }
}
const item = getRandomValue()

console.log(item); // 无法确切知道 item 的类型
console.log(item.length || item.tofixed() ); // 将会报错
  1. 类型断言解决

    1
    console.log((item as string).length || (item as number).toFixed() );
  2. 类型保护

    1
    2
    3
    4
    5
    6
    7
    8
    function isString (value: string | number): value is string {
    return typeof value === 'string'
    }
    if (isString(item)) {
    console.log(item.length);
    } else {
    console.log(item.toFixed());
    }
  3. typeof 类型保护

    1
    2
    3
    4
    5
    if (typeof item === 'string') {
    console.log(item.length);
    } else {
    console.log(item.toFixed());
    }

注意:

  • typeof 必须确保 === 或是 !===
    1
    2
    3
    4
    5
    6
    7
    8
    console.log((typeof item).includes('string'))     // true

    // 无效
    if ((typeof item).includes('string')) {
    console.log(item.length);
    } else {
    console.log(item.toFixed());
    }
  • 类型只能为 基本类型 中的 string | number | boolean | symbol

null && undefined

  1. null && undefined 是特殊的两种类型
  • 类型检查器认为 null && undefined 可以赋值为任何类型
  • tsconfig / strictNullChecks 标记可以解决这个问题
  • 之后声明一个变量时不会自动包含 null | undefined
1
2
3
4
5
let s = 'foo'
// s = null // error
let sn: string | null
sn = null // 有效
// sn = undefined // error 'undefined'不能赋值给'string | null'
  1. string | undefined 或 string | null 或 string | undefined | null 或 undefined | null 是几个完全不同的类型

  2. 去除 null

  • 类型保护

    1
    2
    3
    4
    5
    6
    7
    8
    function f(sn: string | null): string {
    if (sn == null) {
    return "default";
    }
    else {
    return sn;
    }
    }
  • 类型断言

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function getSplicedStr (num: number | null): string {
    function getRes (prefix: string) {
    // error num 对象可能为 null
    // 对于嵌套函数,编译器无法自动去除 null
    // return prefix + num.toFixed().toString()

    // 1. 类型断言
    // num! 表示不为 null
    return prefix + num!.toFixed().toString()

    // 2. 可选参数
    return prefix + num?.toFixed().toString()
    }
    // 若为 null,则默认值为 0.1 ,但编译器无法识别,需要主动 类型断言
    num = num || .1
    return getRes('-prefix-')
    }

    console.log(getSplicedStr(3.141592653)); // -prefix-3
    console.log(getSplicedStr(1)); // -prefix-1
    console.log(getSplicedStr(null)); // -prefix-0

类型别名

类型别名会给一个类型起个新名字,可以通过 新名字 引用这个对象

  1. 使用泛型

    1
    2
    3
    type PositionType<T> = { x: T, y: T }
    const p1: PositionType<number> = { x: 1, y: -1 }
    const p2: PositionType<string> = { x: '1', y: '-1' }
  2. 使用类型别名在属性中引用自己 (只可在对象属性中,不能直接使用)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 使用类型别名在属性中引用自己
    type Tree<T> = {
    value: T,
    left?: Tree<T>,
    right?: Tree<T>,
    }
    // 类似树状结构 嵌套
    type Childs<T> = {
    current: T,
    child?: Childs<T>,
    }
    const c: Childs<string> = {
    current: '1st',
    child: {
    current: '2nd',
    child: {
    current: '3rd'
    }
    }
    }

    不能直接使用

    1
    type Test = Test  // error 类型别名“Test”循环引用自身
  3. 为接口使用别名时,不能使用 implements | extends

  4. 接口和类型别名有时可以起到同样的作用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    type Alias = {
    num: number
    }
    interface Interface {
    num: number
    }
    let _alias: Alias = {
    num: 123
    }
    let _Interface: Interface = {
    num: 321
    }
    _alias = _Interface // 兼容
  • 尽量使用接口 interface 代替 类型别名
  • 当定义的类型需要通过 implements 进行拓展,则使用 interface
  • 如果无法通过接口描述一个类型,并且需要使用 联合类型或元组类型 时,通常使用 类型别名

字面量类型

  1. 字符串字面量类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    type Name = 'Tan'             // 此处的 'Tan' 作为一个 字符串字面量使用
    // const name: Name = '00' // error 不能将类型“"00"”分配给类型“"Tan"”

    type Direction = 'north' | 'east' | 'south' | 'west'
    function getDirectionInitials (direction: Direction) {
    return direction.slice(0, 1).toUpperCase()
    }

    console.log(getDirectionInitials('north')); // N
  2. 数字字面量类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    type Age = 18
    interface InfoInterface {
    name: string,
    age: Age
    }

    const _info: InfoInterface = {
    name: 'tan',
    age: 100, // error 不能将类型“100”分配给类型“18”
    }

可辨识联合类型

可辨识联合两要素

  1. 具有普通的单例类型属性
  2. 一个类型别名包含哪些类型的联合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Square {
kind: 'square',
size: number
}
interface Rectangle {
kind: 'rectangle',
height: number,
width: number
}
interface Circle {
kind: 'circle',
radius: number
}
type Shape = Square | Rectangle | Circle
function getArea (s: Shape): number {
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; // ** ES7 表示 幂运算 **2 平方 **3 立方
}
}
-------------本文结束感谢您的阅读-------------