1、枚举
(1)枚举成员类型
enum Animal {
Dog = 1,
Cat = 2
}
interface Dog {
type: Animal.Dog
}
interface Cat {
type: Animal.Cat
}
let dog: Dog = {
type: Animal.Dog
}
(2)联合枚举类型
enum Status {
Off,
On
}
interface Light {
status: Status
}
enum Animal {
Dog = 1
Cat = 2
}
const light1: Light = {
status: Status.Off
}
const light2: Light = {
status: Status.On
}
(3)枚举合并
// ts会自动将Day里的枚举值合并
enum Day {
SUNDAY,
MONDAY,
TUSEDAY
}
enum Day {
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}、
2、类型断言
(1) 使用场景
你清楚地知道一个实体具有比它现有类型更确切的类型。类型断言好比其他语言中的类型转换,但是不进行特殊的数据检查和解构。(告诉编译器:“相信我,我知道自己在干什么”)
(2) 写法
① “尖括号”语法
- 形式:
<类型>变量名
let someValue: any = "this is a string"
let strLength: number = (<string>someValue).length
② as语法
- 形式:
变量名 as 类型
let value: any = "this is a string"
let length: number = (value as string).length
3、非空断言
当你明确知道某个值不可能为undefined 和 null 时,你可以用 在变量后面加上一个 !(非空断言符号)
- 形式:
let 变量名: xx = 值!
function fun(value: string | undefined | null) {
const str: string = value!
const length: number = value!.length
}
4、确定赋值断言
允许在实例属性和变量声明后面放置一个!,从而告诉TypeScript该属性会被明确地赋值
- 形式:
let 变量名!: xx
// error 未使用断言前,提前使用count变量会报错
let count: number
initialCount()
// error 在赋值前使用了变量“count”
console.log(count * 2);
function initialCount() {
count = 100
}
// success 使用断言写法
let count!: number
initialCount()
console.log(count * 2)
function initialCount() {
count = 100
}
5、类型守卫
(1) 什么是类型守卫
-
类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内
-
当遇到条件语句时,会限制变量类型(缩小范围)。
- 类型判断:
typeof - 实例判断:
instanceof - 属性判断:
in - 字面量相等判断:
==、===、!=、!==
- 类型判断:
(2) 类型判断 - typeof
function test(own: string | boolean | number) {
if(typeof own === 'string') {
// TODO
} else if(typeof own === 'boolean') {
// TODO
} else {
// TODO
}
}
(3) 属性判断 - in
in右侧一般会跟一个联合类型,使用in操作符可以对该联合类型进行迭代。其作用类似JS中的for...in或者for...of
type Animals = 'pig' | 'cat' | 'dog'
type animals = {
[key in Animals]: string
}
// type animals = {
// pig: string // 第一次迭代
// cat: string // 第二次迭代
// dog: string // 第三次迭代
// }
(4) instanceof 关键字
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
- 形式:
object instanceof constructor
let str = 'aaxx'
str instanceof String // error "instanceof" 表达式左侧必须是 "any" 类型、对象类型或类型参数
let str2 = new String('aaxx')
str2 instanceof String // true
(5) 自定义类型保护的类型谓词
function isNumber(x: any): x is number {
return typeof x === 'number'
}
6、交叉类型
(1) 作用
- 将多个类型合并为一个类型
- 形式:
类型1 & 类型2
(2) 示例
interface aa {
id: string
age: number
}
interface bb {
name: string
production: string
}
type Union = aa & bb
const union: Union = {
id: '11',
age: 123,
name: 'angle',
production: 'bag'
}
7、泛型
- 使用泛型的两个场景:①当你的函数、接口或类将处理多种数据类型时;②当函数、接口或类在多个地方使用该数据类型时。
(1) 泛型接口
interface Identities<V, M> {
value: V,
message: M
}
function identity<T, U> (value: T, message: U): Identities<T, U> {
console.log(value + ": " + typeof (value));
console.log(message + ": " + typeof (message));
let identities: Identities<T, U> = {
value,
message
};
return identities;
}
console.log(identity(68, "Semlinker"));
(2) 泛型类
interface GenericInterface<U> {
value: U,
getIdentity: () => U
}
class IdentityClass<T> implements GenericInterface<T> {
value: T
constructor(value: T) {
this.value = value
}
getIdentity(): T {
return this.value
}
}
const myNumberClass = new IdentityClass<Number>(68)
console.log(myNumberClass.getIdentity())
const myStringClass = new IdentityClass<string>("Semlinker")
console.log(myStringClass.getIdentity());
(3) 泛型约束
① 确保属性存在
T extends Length用于告诉编译器,我们支持已经实现Length接口的任何类型。
interface Length {
length: number
}
function identity<T extends Length>(arg: T): T {
console.log(arg.length);
return arg
}
identity([1, 2])
- 此外,我们还可以使用
,号来分隔多种约束类型,比如:<T extends Length, Type2, Type3>。而如果确定变量为数组时,也可以通过显式的将变量设置为数组类型,来约束类型。
// 写法1
function identity<T>(arg: T[]): T[] {
console.log(arg.length)
return arg
}
identity('zxcvbnm') // error
// 类型“string”的参数不能赋给类型“unknown[]”的参数。ts(2345)
// 写法2
function identity<T>(arg: Array<T>): Array<T> {
console.log(arg.length)
return arg
}
② 检查对象上的键是否存在
- keyof:
TypeScript操作符。可用于获取某种类型的所有键,其返回类型是联合类型
interface Person {
name: string
age: number
location: string
}
type K1 = keyof Person // "name" | "age" | "location"
type K2 = keyof Person[] // number | "length" | "concat" | ...
type K3 = keyof { [x: string]: Person } // string | number
- 下面结合前面介绍的
extends约束,即限制输入的属性名包含在keyof返回的联合类型中。
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
- 在以上的
getProperty函数中,我们通过K extends keyof T确保参数key一定是对象中含有的键,这样就不会发生运行时错误。这是一个类型安全的解决方案,与简单调用let value = obj[key]不同。
下面是getProperty函数的使用方法:
enum Difficulty {
Easy,
Intermediate,
Hard = 8888
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
let tsInfo = {
name: 'Typescript',
supersetOf: 'Javascript',
difficulty: Difficulty.Hard
}
let difficulty: Difficulty = getProperty(tsInfo, 'difficulty') // ok
let supersetOf: string = getProperty(tsInfo, 'superset_of') // error
// 类型“ 'superset_of' ”的参数不能赋给类型“ 'difficulty' | 'name' | 'supersetOf' ”的参数。ts(2345)
(4) 泛型参数默认类型
当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推断出类型时,这个默认类型就会起作用。格式: <T=Default Type>
interface A<T=string> {
name: T
}
const strA: A = { name: 'semlinker' }
const numB: A<number> = { name: 101 }
泛型参数的默认类型遵循以下规则:
- 有默认类型的类型参数被认为是可选的
- 必选的类型参数不能在可选的类型参数后
- 如果类型参数有约束,类型参数的默认类型必须满足这个约束
- 当指定类型实参时,你只需要指定必选类型参数的类型实参。未指定的类型参数会被解析为它们的默认类型
- 如果指定了默认类型,且类型推断无法选择一个候选类型,那么将使用默认类型作为推断结果
- 一个被现有类或接口合并的类或者接口的声明可以为现有类型参数引入默认类型
- 一个被现有类或接口合并的类或者接口的声明可以引入新的类型参数,只要它指定了默认类型
(5) 泛型条件类型
格式:
T extends U ? X : Y
(6) 泛型工具类型
TypeScript内置了一些常用的工具类型。比如 Partial、Required、Readonly、Record、ReturnType等。
① Partial
Partial<T> 的作用是将某个类型里的属性全部变为可选项?
interface Todo {
title: string
description: string
}
// fieldsToUpdate 的类型为 Partial<Todo>
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate}
}
const todo1 = {
title: 'organize desk',
description: 'clear clutter'
}
const todo2 = updateTodo(todo1, {
description: 'throw out trash'
})
② Record
Record<K extends keyof any, T> 的作用是将K中所有的属性的值转化为T类型
interface PageInfo {
title: string
}
type Page = 'home' | 'about' | 'contact'
const x: Record<Page, PageInfo> = {
about: { title: '123'},
home: { title: '234'},
contact: { title: '456' }
}
③ Pick
Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型(K extends keyof T K是T的子集)
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = Pick<Todo, "title" | "completed">
const todo: TodoPreview = {
title: 'Clean room',
completed: false
}
④ Exclude
Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉
如果 T 能赋值给 U 类型的话(U ≥ T),那么就会返回 never 类型
type T0 = Exclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'> // 'c'
type aa = Exclude<'a' | 'b' | 'c', 'a' | 'b' | 'c'> //never
type T2 = Exclude<string | number | (() => void), Function> // string | number
⑤ ReturnType
ReturnType<T> 的作用是用于获取函数 T 的返回类型
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<any>; // any
type T5 = ReturnType<never>; // any
type T6 = ReturnType<string>; // Error
type T7 = ReturnType<Function>; // Error
(7) 使用泛型创建对象
① 构造签名
语法:new (x: number, y: number)
interface Point {
new (x: number, y: number): Point // 构造签名
}
② 构造函数类型
- 包含一个或多个构造签名的对象类型被称为构造函数类型
- 构造函数类型可以使用构造函数类型字面量或包含构造签名的对象类型字面量来编写
- 格式:
new <T1, T2, ...>(p1, p2, ...) => R或{ new <T1, T2, ...>(p1, p2, ...) : R }
<1>构造函数类型字面量
new (x: number, y: number) => Point
<2>对象类型字面量
{
new (x: number, y: number): Point
}
③ 构造函数类型的应用
- 必须把接口的属性和构造函数类型进行分离
// 接口属性
interface Point {
x: number
y: number
}
// 构造函数类型
interface PointConstructor {
new(x: number, y: number): Point
}
// 构造函数类型
class Point2D implements Point {
readonly x: number
readonly y: number
constructor(x: number, y: number) {
this.x = x
this.y = y
}
}
// 工厂函数:根据传入的 PointConstructor 类型的构造函数来创建对应的 Point 函数
function newPoint(pointConstructor: PointConstructor, x: number, y: number): Point {
return new pointConstructor(x, y)
}
const point = newPoint(Point2D, 1, 2);
console.log(point);