类型方法/特性
自动类型判断(类型推论)
- TS拥有自动的类型判断机制
- 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
- 所以如果变量的声明和赋值时同时进行的,可以省略掉类型声明
type(类型别名)
- 为任意类型起别名
- 使用 type 关键字来创建类型别名
- 使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
type nihao = 1|2|3
let k:nihao;
interface(接口)
接口声明是命名对象类型的另一种方式
interface Point {
x: number;
y: number;
}
function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 100, y: 100 });
类型别名和接口的区别
类型别名和接口非常相似,在很多情况下你可以在它们之间自由选择。interface 的几乎所有功能都在 type 中可用,主要区别在于无法重新打开类型以添加新属性,而接口始终可扩展
- type 不得参与在声明合并中,但 Interface 可以
interface Mammal {
genus: string
}
interface Mammal {
breed?: string
}
const animal: Mammal = {
genus: "1234",
breed: "123"
}
type Reptile = {
genus: string
}
// 报错,不能与上面的 Reptile 合并
type Reptile = {
breed?: string
}
- Interface 只能用于声明对象的形状,而不是重命名基础类型
// 报错
interface X extends string {}
- Interface 名称将在错误消息中显示为总是以原来的形式出现,但仅当它们被名称使用时
interface Mammal {
name: string
}
function echoMammal(m: Mammal) {
console.log(m.name)
}
// 报错,提示 name 必须为字符串
echoMammal({ name: 12343 })
泛型(Generic)
|(联合类型)
由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种
let arr: (number | string)[] = [1, 'a', 3, 'b']
代码缩小联合
如果有共同属性就不用缩小
function printId(id: number | string) {
if (typeof id === "string") {
// In this branch, id is of type 'string'
console.log(id.toUpperCase());
} else {
// Here, id is of type 'number'
console.log(id);
}
}
function welcomePeople(x: string[] | string) {
if (Array.isArray(x)) {
// Here: 'x' is 'string[]'
console.log("Hello, " + x.join(" and "));
} else {
// Here: 'x' is 'string'
console.log("Welcome lone traveler " + x);
}
}
as/<>(类型断言)
有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
// 第一种(常用)
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// 第二种
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;
// 类型断言,用来告诉解析器变量的实际类型
bb = aa as string;
// 或者
bb = <string>aa;
此规则可能过于保守,并且不允许可能有效的更复杂的强制转换。如果发生这种情况,可以使用两个断言,首先是 any(或 unknown,我们稍后会介绍),然后是所需的类型:
const a = expr as any as T;
&(同时)
同时
let j:{ name:string} & { age:number }
j = {name:'swk', age:18}
?(可选)
可选
// {} :指定对象中可以包含哪些属性
let bbb: {name: string,age?:number};
bbb = {name:'nihao',age:18};
!(非空断言)
该值不是 null 或 undefined
function liveDangerously(x?: number | null) {
// No error
console.log(x!.toFixed());
}
[propName: string]: any
任意类型的属性
// [propName: string]: any ----表示任意类型的属性
let ccc:{name:string,[xxx: string]: any};
ccc = {name:'dishitian', age:18,sex:'男'}
类型缩小
typeof
- 使用 typeof 来进行类型判断,使用 typeof 会返回一组特定字符串
-
- "string"
- "number"
- "bigint"
- "boolean"
- "symbol"
- "undefined"
- "object"
- "function"
- 真值缩小,以下的值转换成 boolean 会转换为 false
-
- 0
- NaN
- ""(空字符串)
- 0n(bigint 版本零)
- null
- undefined
相等性缩小
TypeScript 还使用 switch 语句和 ===、!==、== 和 != 等相等性检查来缩小类型
in 运算符缩小
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
function move(animal: Fish | Bird | Human) {
if ("swim" in animal) {
animal; // (parameter) animal: Fish | Human
} else {
animal; // (parameter) animal: Bird | Human
}
}
instanceof 缩小
x instanceof Foo 检查 x 的原型链是否包含 Foo.prototype
赋值
// let x: string | number
let x = Math.random() < 0.5 ? 10 : "hello world!";
// let x: number
x = 1;
console.log(x);
// let x: string
x = "goodbye!";
console.log(x);
使用类型谓词
更直接地控制类型在整个代码中的变化方式,只需要定义一个返回类型为类型谓词的方法:
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
pet is Fish是本例中的类型谓词。谓词采用parameterName is Type的形式,其中 parameterName 必须是当前函数签名中的参数名称- 任何时候使用某个变量调用
isFish时,如果基础类型兼容,TypeScript 就会将该变量缩小到该特定类型
let pet = getSmallPet();
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
可以使用类型保护 isFish 过滤 Fish | Bird 的数组并获得 Fish 的数组:
const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()];
const underWater1: Fish[] = zoo.filter(isFish);
// 或者
const underWater2: Fish[] = zoo.filter(isFish) as Fish[];
// 更复杂的例子,可能需要重复谓词
const underWater3: Fish[] = zoo.filter((pet): pet is Fish => {
if (pet.name === "sharkey") return false;
return isFish(pet);
});
断言函数
判断联合
定义一个形状
interface Shape {
kind: "circle" | "square";
radius?: number;
sideLength?: number;
}
再设计一个求面积的方法,但是两种图形求面积公式不同,所以需要在传进来的时候判断类型
function getArea(shape: Shape) {
if (shape.kind === "circle") {
// 'shape.radius' is possibly 'undefined'.
return Math.PI * shape.radius ** 2;
}
}
// 使用非空断言强制表示 radius 存在
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius! ** 2;
}
}
但这感觉并不理想。这里不得不用那些非空断言(!)强制类型检查器确认它定义了 shape.radius,如果开始移动代码,这些断言很容易出错
这里重新定义区分
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
再次尝试检查 kind 属性
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}
never 类型
拿来兜底
穷举检查
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
添加新成员,如果不给新成员指定,则会报错
interface Triangle {
kind: "triangle";
sideLength: number;
}
type Shape = Circle | Square | Triangle;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape;
// Type 'Triangle' is not assignable to type 'never'.
return _exhaustiveCheck;
}
}