持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
有时候我们在定义一个变量的时候,不太确定这个值最终会是什么类型,有可能是null,也有可能是对象。或者说我们声明一个函数,这个参数有可能是数字类型也有可能是字符串类型,那么我们该如何定义呢?
联合类型
我们在很多编程中都有一个运算符叫逻辑运算符,他包括&
、|
和!
,而在TS中也有|
这个运算符,不过它不叫或,而是叫联合类型。不过我们也可以称它为或运算符。
let a:string|number;
这个的意思就是a变量既可以是string类型也可以是number类型。
let a:string|number;
a=1;
a='string';
所以我们可以给a即赋值字符串页也可以赋值数值。但是不能再是其他类型了。
再比如在函数中:
let add: (x: number | string) => number | string = function (x) {
if (typeof x === "string") return x + "!";
else return x + 1;
};
这样我们的add函数的参数就可以是字符串也可以是数值了。
而且联合类型常常用于type中:
type Person = {name:string,age:number}
type State = {name:string,state:string}
type Worker = Person | State;
那么现在Worker既包含Person的类型,也包含State的类型。
let worker1:Worker = {
name:'xiaolei',
age:18,
state:'good'
}
let worker2:Worker = {
name:'xiaoxu',
age:18
}
let worker3:Worker = {
name:'xiaowang',
state:'good'
}
let worker4:Worker = {
age:18,
state:'good'
} // 类型 "{ age: number; state: string; }" 中缺少属性 "name",但类型 "State" 中需要该属性。
我们发现声明这个Worker类型的变量时,既可以只赋值Person,也可以只赋值State,也可以两种的都赋值(但是不能每一个赋值一部分)。但是在使用的时候就有问题了:
console.log(worker1.age); //类型“State”上不存在属性“age”
console.log(worker2.state); // 类型“Person”上不存在属性“state”。
console.log(worker3.name);
最后只有worker3.name没有报错,这是因为在type中使用联合类型来定义类型,你只能取它们的共有属性。
交叉类型
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。它类似于接口的继承。
type Person = { name: string; age: number };
type State = { name: string; state: string };
type Worker = Person & State;
它相当于:
type Worker ={name: string; age: number;state: string};
但是交叉类型也有它自己的特性,那就是它赋值的时候必须全部赋值:
let worker1: Worker = {
name: "xiaolei",
age: 18,
state: "good"
};
let worker2: Worker = { //报错:缺少属性 "state",但类型 "State" 中需要该属性。
name: "xiaoxu",
age: 18
};
let worker3: Worker = { //报错:缺少属性 "age",但类型 "Person" 中需要该属性。
name: "xiaowang",
state: "good"
};
let worker4: Worker = { //报错:缺少属性 "name",但类型 "Person" 中需要该属性。
age: 18,
state: "good"
};
但是我们在使用的时候就可以取他们各自拥有的或者两者共有的:
console.log(worker1.age);
console.log(worker1.state);
console.log(worker1.name);
此外我们可以把Worker类型的数据分别赋值给Person和State类型的数据:
let worker1: Worker = {
name: "xiaolei",
age: 18,
state: "good"
};
let p1:Person = {
name:'xiaoxu',
age:18
}
let state:State = {
name:'xiaoxu',
state:'bad'
}
p1 = worker1;
state = worker1;
所以说我们可以把交叉类型认为是继承,因为使用交叉类型后新得到的类型是拥有了使用交叉类型的特性的,那么新类型也就包含继承前的类型,也就可以相互赋值了(后面断言的要求也是这个)。
此外,如果是两个相同的类型使用交叉类型也是可以的:
type Fn1 = (value:number)=>string
type Fn2 = (value:string)=>string
type Fn3 = Fn1 & Fn2;
这样的结果就是:
type Fn3 = (value:number|string)=>string
这也是type和interface的一个区别点:
interface Fn1 { //接口“Fn1”错误扩展接口“Fn2”。
fn: (value: number) => string;
}
interface Fn2 {
fn: (value: string) => string;
}
interface Fn1 extends Fn2 {} //报错
而使用type的话则是可以的:
type Fn3 = Fn1 & Fn2;