从零开始的 TypeScript 学习(五)—— 高级一点的类型
字面量类型
在 TypeScript 中,用字面量表示的类型,被称为字面量类型。
const one: 1 = 1;
const man: "male" = "male"
const arr: [1, 2, 3, 4] = [1, 2, 3, 4]
上面示例中,都是正确的字面量类型的赋值方式。当变量类型定义是字面量类型时,变量就只能使用值类型的字面量作为变量自己的值。赋值为其他类型值就会报错。
const one: 1 = 2; // 报错
const arr: [1, 2] = [1, 2, 3] // 报错
遇到const
命令声明的变量,如果代码里面没有注明类型,就会推断该变量是字面量类型。
const a = 1; // const a: 1
上面示例中,变量 a
是 const
关键字声明的,TypeScript 就会推断它的类型是数字字面量类型1
。
由于 const
声明的对象是可以修改的,所以这种推断不适用于对象中的属性和方法,而是根据正确的值去判断对应的类型。
const obj = {
a: 100,
y: "123",
}
/*
类型
const obj: {
a: number;
y: string;
}
*/
上面示例中,obj
对象中 a
被推断成 number
类型 ,y
的类型被推断成 string
类型;也可以手动给对象赋值一个对象字面量类型。
const obj: {age: number, name: string, isWorking: boolean} = {
age: 25,
name: "Peter",
c: true
}
上面示例中,obj
获得了字面量类型 {age: number, name: string, isWorking: boolean}
,obj
中的属性值类型、属性个数、属性名都不可以随便添加和修改。初始化时也要符合对象字面量类型的声明规范,否则就会报错。
值类型可能会出现一些很奇怪的报错。
const x: 5 = 4 + 1; // 报错
上面示例中,等号左侧是数字字面量类型 5
,等号右侧是 4 + 1
(加法运算的优先级比赋值更高)的类型值;TypeScript 会推断类型是 number
。我们可以使用类型断言收束等号右边的值的类型。
const x: 5 = (4 + 1) as 5
const y: 5 = <5>(4 + 1)
单一的字面量类型很少被使用,在开发中一般使用联合类型扩大字面量类型的用法。
联合类型
联合类型是使用竖线符号 |
分割多个类型,将多个类型组成的一个新类型。
let x: number | string
x = 1
x = "1"
上面示例中,x
既可以是 number
类型又可以是 string
类型,它的值可以是字符串,也可以是数字。
联合类型可以与字面量类型结合,表示变量只能在若干个字面量类型中赋值,类似于一种枚举。
let ishot: true | false = true // 相当于 boolean 类型
let gender: 'male' | 'female' = 'male'
let primaryColors: "red" | "green" | "blue" = 'red'
如果一些变量可能是空值 undefined
或 null
也可以使用联合类型进行限制;
let x: string | null = "123"
x = null
如果一个变量拥有多个类型,那么在使用某些类型特有方法时,可能会报错,所以需要使用类型收缩或类型断言去缩小变量的类型范围。
function printId(id: number | string) {
console.log(id.toUpperCase()); // 报错
}
上面示例中,参数变量id
可能是数值,也可能是字符串,这时直接对这个变量调用toUpperCase()
方法会报错,因为这个方法只存在于字符串原型上,不存在于数值的原型中。
function func(id: number | string) {
console.log((id as string).toUpperCase()); // 类型断言
console.log((<string>id).toUpperCase()); // 类型断言
if (typeof id === 'string') { // 类型收缩
console.log(id.toUpperCase());
}
}
上述示例中,缩小了 id
的范围,告诉 Typescript 是一种字符串类型,以便可以调用字符串原型上面的方法。
可以判断类型的条件语句都可以作为类型收缩的一种方法:
function getPort(scheme: "http" | "https") {
switch (scheme) {
case "http":
return 80; // http 的默认端口是 80
case "https":
return 443; // hhtps 的默认端口号是 443
}
}
上面示例中,函数体内部对参数变量 scheme
进行类型缩小,根据不同的字面量类型,返回不同的结果。
交叉类型
如果联合类型使用的是"或"运算符,那么交叉类型必定就使用"与"运算符了吧~
交叉类型使用符号&
表示,指变量同时拥有多种类型。
let x: string & number
x = 123; // 报错
x = "123"; // 报错
这是一件非常荒谬的事情,x
变量的值不可能既是 string
类型,又是 number
类型。两种确定类型的交叉类型就像是 never
类型,永远不能取到值。在编辑器中,这种类型也确实会被推断成 never
类型。
在声明对象时,交叉类型可以起到很好的效果。
let person: { name: string } & { age: number };
obj = {
name: "Rick",
age: 70,
};
上面示例中,声明了一个 name
类型交叉 age
类型的变量吗,这与直接声明一个 {name: string, age: number}
没有区别。
Type
type
可以用来定义类型的别名,别名可以让类型的名字变得更有意义,也能增加代码的可读性,还可以使复杂类型用起来更方便,便于以后修改变量的类型。
通常类型别名的首字母要大写:
type Person = {name: string, age: number};
let person: Person = {
name: "Rick",
age: 70,
}
上面示例中,声明了一个名为 Person
的类型别名,又声明了一个对象,它的类型是 Person
。
别名定义之后只能当作类型使用,不能当作变量,声明之后不可以使用等号进行赋值操作:
type Point = {
x: number;
y: number;
}
Point = {}; // 报错
上面示例中,声明了一个名为 Point
的类型别名,当想更改 Point
的值时,Typescript 会提示报错,因为 Point
是类型,不能当作变量(值)去使用。但是类型可以当作一个类型去给另一个类型赋值:
type Person = {
name: string;
age: number
}
type Teacher = Person // 不报错
类型别名的命名不允许重复:
type Color = "red";
type Color = "blue"; // 报错
类型别名属于块级作用域:
type Color = "red";
if (Math.random() < 0.5) {
type Color = "blue";
}
可以使用交叉类型定义不同的对象类型:
type Point = {
x: number,
y: number
}
type Line = { x2: number, y2: number } & Point
const point: Point = { x: 1, y: 1 }
const line: Line = { x: 0, y: 0, x2: 2, y2: 3 }
别名也可以用作类型断言:
type Str = string
function func(id: number | string) {
console.log((id as Str).toUpperCase());
console.log((<Str>id).toUpperCase());
if (typeof id === 'string') {
console.log(id.toUpperCase());
}
}
类型别名就好像一个 const
声明的变量,属于块级作用域,命名不能重复,声明之后不可以修改,但是可以给其他类型赋值。
类型别名只能当作类型使用,只能放在使用类型的地方,不能用作变量。
type
命令属于类型相关的代码,编译成 JavaScript 的时候,会被全部删除。
typeof
在 JavaScript 中,typof
运算符返回一个值的类型。
typeof x
会以字符串的形式返回数据类型:
console.log(typeof true) // "boolean"
console.log(typeof 123) // "number"
console.log(typeof "234") // "string"
console.log(typeof undefined) // "undefined"
console.log(typeof null) // "object"
console.log(typeof [4, 5, 6]) // "object"
console.log(typeof Math) // "object"
console.log(typeof alert) // "function"
console.log(typeof 10n) // "bigint"
console.log(typeof Symbol("id")) // "symbol"
TypeScript 也拥有 typeof
运算符,可以在类型上下文中使用它来引用变量或属性的类型,它的返回值是一个 TypeScript 类型:
const person = {
name: 'Rick',
age: 70
};
const student: typeof person = {
name: "Morty",
age: 14,
};
type Person = typeof person;
type Name = typeof person.name;
上面示例中,typeof person
和 typeof person.name
表示变量 person
的类型 { name: string; age: number; }
和 person.name
的类型 string
。
在 TypeScript 的运算中,如果 typeof
运行在值运算中,大概率表示 JavaScript 中的代码,返回的就是类型字符串,如果运行在类型运算或赋值中,那么返回的就是 TypeScript 类型。
本章最后
上述内容如果有任何问题和错误,希望大佬们可以指出。
也不知道应该写长一点还是写短一点才能引起你们的注意,不过我一定会尽力坚持下去的~~~
转载自:https://juejin.cn/post/7381855349462794249