高级类型
交叉类型
交叉类型是将多个类型合并为一个类型。这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。这个类型的对象同时拥有了这三种类型的成员
interface Person {
name: string
}
interface Animal {
sex: number
}
function getResult<T, U>(first: T, second: U): T & U {
let result = <T & U>{}
for (let id in first) {
;(<any>result)[id] = (<any>first)[id]
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
;(<any>result)[id] = (<any>second)[id]
}
}
return result
}
let first: Person = { name: "zs" }
let second: Animal = { sex: 0 }
console.log(getResult(first, second)) // { name: 'zs', sex: 0 }
我们在getResult
函数中传入了两个不同类型的参数,返回的类型时这两个类型的结合Person & Animal
。返回的类型中有这两种类型的所有成员。
联合类型
我们常常会遇到这样的情况,一个方法的某个参数既可以是string
也可以是number
,这时候我们就需要用到联合属性了
function getResult(x: number | string) {
if (typeof x === "number") console.log("number")
else if (typeof x === "string") console.log("string")
else throw new Error("参数类型不对")
}
getResult(1) // number
getResult("1") // string
getResult(true) // error 类型“true”的参数不能赋给类型“string | number”的参数。ts(2345)
也许你会想到any
也可以,如果使用any
在编译阶段是可以的,到运行阶段就会报错了。而联合属性在编译阶段就已经会给我们提示错误了。联合属性可以用在方法返回值、定义接口、定义参数等地方。
直白的理解交叉类型和联合类型:交叉类型是并且的关系,所有的类型都会被包括,而联合类型是或者的关系,几个中选着一个就行。
当我们使用联合类型的时候,我们无法确切的知道到底是什么类型,我们一般都是通过判断是否有某个对象的成员来进行区分的。
let obj: Person | Animal
// 使用类型断言来强制转换类型
if ((<Person>obj).name) {
console.log("Person")
} else if ((<Animal>obj).sex) {
console.log("Animal")
}
这里我们不得不需要通过类型断言来转换类型,这是比较没必要的。在 TS 中提供了类型保护机制
,通过类型保护机制
我们可以不多次使用类型断言就可以进行类型的区分。
let obj: Person | Animal
function isPerson(x: Person | Animal): x is Person {
return (<Person>x).name !== undefined
}
if (isPerson(obj)) {
console.log("Person")
} else {
console.log("Animal")
}
类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个 类型谓词。在这个例子里, x is Person
就是类型谓词。 谓词为 parameterName is Type
这种形式, parameterName
必须是来自于当前函数签名里的一个参数名。
类型别名
类型别名和之前泛型中的类型变量其实就是一回事,我们通过一个变量来存储类型(创建了一个新 名字来引用那个类型),一般我们不会用别名来存储原始类型,这样没什么用,尽管可以做为文档的一种形式使用。
类型别名也可以是泛型 - 我们可以添加类型参数并且在别名声明的右侧传入:
type Container<T> = { value: T }
类型别名不能出现在声明右侧的任何地方
type Yikes = Array<Yikes> // error
字符串字面量类型
字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串。
type Easing = "ease-in" | "ease-out" | "ease-in-out"
function getEasing(ease: Easing): boolean {
return ease == "ease-in"
}
getEasing("hello world") // error 类型“"hello world"”的参数不能赋给类型“Easing”的参数。ts(2345)
getEasing("ease-in") // OK
只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误。
数字字面量类型和字符串字面量类型其实差不多。
索引类型
索引类型查询和索引访问操作符
keyof T
索引类型查询操作符,对于任何类型 T,keyof T
的结果为 T 上已知的公共属性名的集合。
interface Person {
name: string;
age: number;
}
// keyof Person ==> 'name'|'age'
T[K]
索引访问操作符,类型语法反应了表达式语法。意味着person['name']
具有类型Person['name']
–>string
类型。就像索引类型查询一样,你可以在普通的上下文里使用 T[K],这正是它的强大所在。 你只要确保类型变量 K extends keyof T 就可以了。