类型推断
类型推断就是TypeScript
会根据上下文自动帮我们推算出变量或方法的类型,而不需要我们显示去定义。
let str = "this is string";
let num = 123;
let bool = true;
// 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 `any` 类型而完全不被类型检查
let flag; //推断为any
let count = 123; //为number类型
let hello = "hello"; //为string类型
// 根据参数的类型,推断出返回值的类型也是 number
function add(a: number, b: number) { return a + b; }
在 TypeScript
中,具有初始化值的变量、有默认值的函数参数、函数返回的类型都可以根据上下文推断出来。
有了类型推断,就不需要我们每个变量每个方法都去定义类型,大大提高了我们的开发效率。
类型断言
有些情况下 TS
并不能正确或者准确得推断类型,这个时候可能产生不必要的警告或者报错。
比如初学者经常会遇到的一类问题:
const person = {};
person.name = 'randy'; // Error: 'name' 属性不存在于 ‘{}’
person.age = 20; // Error: 'age' 属性不存在于 ‘{}’
这个时候该怎么办?由于类型推断,这个时候 person
的类型就是 {}
,根本不存在后添加的那些属性,虽然这个写法在js
中完全没问题,但是开发者知道这个 person
实际是有属性的,只是一开始没有声明而已,但是 typescript
不知道啊,所以就需要类型断言了:
interface Person {
name: string;
age: number;
}
const person = {} as Person;
person.name = 'randy';
person.age = 20;
但是类型断言不要滥用,在万不得已的情况下使用要谨慎,因为你强制把某类型断言会造成 TypeScript
丧失代码提示的能力。
上面我们使用的是as
语法,其实我们还可以使用<>
。
// 尖括号 语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
as
和<>
都可以用来类型推断,但是尖括号格式会与 react
中 JSX
产生语法冲突,因此我们更推荐使用 as
语法。
双重断言
虽然类型断言是有强制性的,但并不是万能的,因为一些情况下也会失效:
interface Person {
name: string;
age: number;
}
const person = 'randy' as Person; // Error
这个时候会报错,很显然不能把 string
强制断言为一个接口 Person
,但是并非没有办法,此时可以使用双重断言:
interface Person {
name: string;
age: number;
}
const person = 'randy' as any as Person; // ok
先把类型断言为 any
再接着断言为你想断言的类型就能实现双重断言,当然上面的例子肯定说不通的,双重断言我们也更不建议滥用,但是在一些少见的场景下也有用武之地,当你遇到事记得有双重断言这个操作即可。
非空断言
非空断言用!
表示,它用来断定某变量一定不是 null
和 undefined
。
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 !
可以用于断言操作对象是非 null
和非 undefined
类型。
具体而言,x! 将从 x 值域中排除 null 和 undefined,也就是说它断定某变量一定不是 null
和 undefined
。
let flag: null | undefined | string;
flag!.toString(); // ok
flag.toString(); // error
确定赋值断言
允许在实例属性和变量声明后面放置一个 !
号,从而告诉 TypeScript
该属性会被明确地赋值。为了更好地理解它的作用,我们来看个具体的例子:
let x: number;
initialize();
// Variable 'x' is used before being assigned.(2454)
console.log(2 * x); // Error
function initialize() {
x = 10;
}
很明显该异常信息是说变量 x
在赋值前被使用了,要解决该问题,我们可以使用确定赋值断言:
let x!: number;
initialize();
console.log(2 * x); // Ok
function initialize() {
x = 10;
}
通过 let x!: number;
确定赋值断言,TypeScript
编译器就会知道该属性会被明确地赋值。
类型守卫
类型守卫说白了就是缩小类型的范围,常用的有typeof、instanceof、in
。
typeof
通过typeof
精细化数据类型进行操作,避免了不必要的错误。
function double(input: string | number | boolean) {
if (typeof input === "string") {
return input + input;
} else {
if (typeof input === "number") {
return input * 2;
} else {
return !input;
}
}
}
instanceof
instanceof
类型保护是通过构造函数来细化类型的一种方式.
class Person3 {
name = "randy";
age = 24;
}
class Animal3 {
name = "dog";
color = "green";
}
function getSometing(arg: Person3 | Animal3) {
// 不类型守卫 不管使用啥属性都报错。
console.log(arg.age); // Error
console.log(arg.color); // Error
// 类型细化为 Person
if (arg instanceof Person3) {
console.log(arg.color); // Error,因为arg被细化为Person,而Person上不存在 color属性
console.log(arg.age); // ok
}
// 类型细化为 Person
if (arg instanceof Animal3) {
console.log(arg.age); // Error,因为arg被细化为Animal,而Animal上不存在 age 属性
console.log(arg.color); // ok
}
}
in
跟上面的例子类似,x in y
表示 x 属性在 y 中存在。
class Person4 {
name = "randy";
age = 24;
}
class Animal4 {
name = "dog";
color = "green";
}
function getSometing(arg: Person4 | Animal4) {
// 不类型守卫 不管使用啥属性都报错。
console.log(arg.age); // Error
console.log(arg.color); // Error
if ("age" in arg) {
console.log(arg.color); // Error
console.log(arg.age); // ok
}
if ("color" in arg) {
console.log(arg.age); // Error
console.log(arg.color); // ok
}
}
类型别名
类型别名会给一个类型起个新名字,单不是创建一个新类型。类型别名有时和接口很像,但是可以作用于原始值、联合类型、元组以及其它任何你需要手写的类型.
你可以使用 type SomeName = someValidTypeAnnotation
的语法来创建类型别名:
type some = boolean | string
const b: some = true // ok
const c: some = 'hello' // ok
const d: some = 123 // 不能将类型“123”分配给类型“some”
类型别名可以给已有类型取别名:
type newstring = string
const str: newstring = 'randy'
此外类型别名可以是泛型:
type Container<T> = { value: T };
也可以使用类型别名来在属性里引用自己:
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}
关于类型别名和接口的区别,笔者前面在TypeScript学习之接口一文中有讲解,感兴趣的小伙伴可以自行查看。
系列文章
TypeScript入门之类型推断、类型断言、双重断言、非空断言、确定赋值断言、类型守卫、类型别名
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!