一、 TS 对应的类型
- 原始类型
- number
- string
- boolean
- null
- undefined
- void
- symbol
- bigint
TS通过冒号语法
来将其类型侧定义的类型附着在JS上
const test: string = '123';
一旦给变量加上了类型定义, 员JS变量的类型就被静态化了, 在初始化时, 就不可赋值其他类型的变量给test
变量
- 非原始类型
- array
- tuple
- enum
const arr = ['1', 2, '3'];
上面这个数组中的变量, 我们可以看到有两个字符串类型和一个数字类型, 但是TS总的数组类型要求数组中的元素都是同一个类型, 不允许动态变化, 我们可以对arr
声明类型——string[]
类型
const arr: string[] = ['1', '2', '3'];
// or
const arr: Array<string> = ['1', '2', '3'];
但实际的开发中, arr
中的元素不是同一个类型的情况可太多了, 所以在TS中这种情况也是存在的, 就是tuple
(元组)类型
tuple
元是一种特殊的数组类型, 它主要用于这样的场景: "一个数组的项数已知, 其中每项的类型也已知" 同样使用这个例子
const arr = ['1', 2, '3'];
现在我们给这个arr
附着一个类型, 使得其静态化
const arr: [string, number, string] = ['1', 2, '3'];
interface
为了给单个对象元素进行类型注解, 我们使用interface
;
它相当于类型中的JS对象, 用于对函数、类等进行结构类型检查, 所谓的结构类型检查
可选属性
一个对象里面的某些参数我们可能没有, 比如一个待办事项Todo, 有时候没有设置time
时间属性, 那么修饰这样的一个对象我们可以改如何? TS提供了可选属性
这样一个方便的属性, 具体处理如下:
interface Todo {
content: string;
user: string;
time?: string;
isCompleted: boolean;
}
可以看到, 只需要在属性类型修饰冒号左边加一个问号就可以了, 此时就相当于告诉TS编译器这个time
属性是可选的一个类型
只读属性
只需要在对应的属性前面加上readonly
关键字即可
interface Todo {
content: string;
readonly user: string;
time?: string;
isCompleted: boolean;
}
一旦被定义为只读, 我们修改对应的属性值, 就会报错
const todo: Todo = {
content: '内容创作',
user: 'z',
isCompleted: false,
}
todo.user = 'm'; // error
多余属性检查
在JS中经常会遇到一个对象, 一开始不能确认它是有哪个属性, 但是它的属性却可以动态增加; TS提供一个多余属性检查的写法
interface Todo {
isCompleted: boolean;
[propName: string]: any;
}
这里我们将其注解为一定拥有isCompleted
属性, 其他的属性可以动态添加, 因为动态添加的属性的值类型我们不清楚, 所以我们用any
来表示值类型, 它可以是任意类型
const todo: Todo = {
content: '内容',
isCompleted: false,
}
todo.user = 'pftom';
todo.time = '2020-04-04';
Enum
枚举, 主要用于帮助定义一系列命名常量, 常用于给一类变量做类型注解, 它们的值是一组值里面的某一个;
可以看下面一个例子, 将五个用户放到枚举里面:
enum UserId {
user1,
user2,
user3,
user4,
user5,
}
接下来, 我们可以给interface里面的Todo
接口中的user
字段一个更精确的类型注解:
interface Todo {
content: string;
user: UserId;
time: string;
isCompleted: boolean;
}
这样一来, todo里面的user字段就是上面的五人之一;
数字枚举
上面我们的UserId中的几个枚举值其实都对应着相应的数字, 比如UserId.user1
它的值是数字0, UserId.user2
数字值是1, 以此类推, 后面几个枚举值分别是数字2, 3, 4;
我们可以手动给其中的某个枚举值赋值一个数字, 这样这个枚举值后面的值会依次在这个赋值的数字上递增, 下面的这个例子的枚举值对应的数字依次是: 0, 6, 7, 8, 9
enum UserId {
user1,
user2 = 6,
user3,
user4,
user5,
}
字符串枚举
enum UserId {
user1 = '123123123',
user2 = '154645653',
user3 = '436432424',
user4 = '346543223',
user5 = '467645321',
}
异构枚举
在一个枚举里面既可以有字符串值也可以有数字
enum UserId {
user1 = '123123123',
user2 = 4,
user3 = '436432424',
user4 = 2,
user5 = '467645321',
}
注解函数
function add(x, y) {
return x + y;
}
函数的主要部分就是输入和输出, 所以在注解函数的时候, 只需要注解函数的参数和返回值就可以了, 因为上述的函数体内是执行x+y
操作, 以x
和y
应该都是number
数字类型, 返回值也是number
数字类型, 所以我们对上面的函数进行如下的类型注解:
function add(x: number, y: number): number {
return x + y;
}
有时候返回值也可以不写, TS可以根据参数类型和函数体计算返回值类型, 也就是俗称的自动推断类型机制;
推断类型
const add = function(x, y) {
return x + y;
}
对于这个例子, 我们可以这样注解:
const add:(x: number, y: number): number = function(x, y) {
return x + y;
}
对于上面的注解, TS会进行类型的自动推导, 根据函数类型的结构对比后面的函数, 会自动推断出后面函数的x
, y
和返回值都为number
可选参数
如果一个函数可能存在一些参数, 但是我们并不是每次都需要传递这些参数, 那么它们就属于可选参数的范围, 如下面的lastName
就是可选参数
function buildName(firstName: string, lastName?: string) {}
// e.g
buildName('Tom', 'li');
buildName('Lucy');
重载
重载主要为函数多返回类型服务, 具体来说就是一个函数可能会在内部执行一个条件语句, 根据不同的条件返回不同的值, 有些值可能是不同类型, 这时候我们就可以使用重载
来给返回值注解类型, 通过定义一系列同样函数名, 不同参数列表和返回值的函数来注解多类型返回值函数
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x): any {
if(typeof x == 'object') {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
} else if(typeof x == 'number') {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
pickCard
参数类型可能有多种选项, 对应不同选项的参数类型, 会有不同的返回值类型, 并且我们对参数类型还未知; 针对这种情况, 可以使用重载
来表达出来
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x): any {
// 如果 x 是 `object` 类型,那么我们返回 pickCard 从 myDeck 里面取出 pickCard1 数据
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// 如果 x 是 `number` 类型,那么直接返回一个可以取数据的 pickCard2
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [
{ suit: "diamonds", card: 2 },
{ suit: "spades", card: 10 },
{ suit: "hearts", card: 4 }
];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
因此, 重载实际上就是函数名一样, 参数列表和返回值不一样
交叉类型
交叉类型就是多个类型, 通过&
类型运算符, 合并成一个类型, 这个类型包含了多了类型中的所有类型成员
以请求一组数据为例, 我们需要根据查询的结果打印对应的信息:
- 请求成功, 返回标志请求成功的状态, 以及目标数据;
- 请求失败, 返回标志请求失败的状态, 以及错误信息;
interface ErrorHandling {
success: boolean;
error?: {message: string};
}
interface ArtistsData {
artists: {name: string}[];
}
const handleArtistsResponse = (res: ArtistsData & ErrorHandling) => {
if(res.error) {
console.error(response.error.message);
return;
}
console.log(res.artists);
}
联合类型
联合类型
实际上是通过操作符|
, 将多个类型进行联合, 组成一个复合类型, 当用这个复合类型注解一个变量的时候, 这个变量可以取这个复合类型中任意一个类型
// padLeft 函数的第二个参数只能是 string 或 number 类型
function padLeft(value: string, padding: string | number) {
// some code
}
// e.g
padLeft('hello', 2); // yes
padLeft('hello', '123'); // yes
padLeft('hello', true); // no
字面量类型
-
数字字面量
像
520
这个数, 把它当做类型使用, 它就是数组字面量类型let a: 520
当我们初始化这个
a
变量的时候, 就只能复制520
这个数字a = 520; // yes a = 521; // error: Type '521' is not assignable to type '520'
-
字符串字面量
字符串字面量也是类似的
let a: '520'; a = '520'; a = '521'; // Type '"521"' is not assignable to type '"520"'