TypeScript的联合类型
联合类型指的是一个变量有多种可能的类型,多种类型间用符号|分割
可以是基础类型:
let content: string | number = "content"; // content 既可以是字符串,也可以是数字
content = 123;
也可以是对象类型:
interface Student {
sid: string
name: string
}
interface Teacher {
tid: string
subject: string
}
let man: Student | Teacher = { // man 既可以是 Student 型,也可以是 Tacher 型
sid: '1001',
name: '小蔡'
}
man = {
tid: '2001',
subject: '体育'
}
TypeScript的类型保护
as 类型断言
当一个变量有多种可能的类型时,可以通过as关键字来确定类型
interface StrProp {
isStr: boolean
name: '字符串'
}
interface NumProp {
isStr: boolean
age: 18
}
function say(arg: StrProp | NumProp) {
// console.log(arg.name) // 报错
if(arg.isStr) {
// 当 arg 的 isStr 属性为true时,我们就断定 arg 是 StrProp 类型
console.log((arg as StrProp).name)
} else {
console.log((arg as NumProp).age)
}
}
in 语法
我们还可以通过in判断变量有没有某个属性从而确定其类型
interface StrProp {
isStr: boolean
name: '字符串'
}
interface NumProp {
isStr: boolean
age: 18
}
function say(arg: StrProp | NumProp) {
if('name' in arg) {
console.log(arg.name)
} else { // 这里的 else 部分 TypeScript 能自动判断
console.log(arg.age)
}
}
typeof 语法
function test(arg: string | number) {
if(typeof arg === 'string') {
return arg.split('')
} else {
return 2 * arg
}
}
Enum 枚举类型
枚举类型一般用来定义常量
// enum Sex { // 枚举类型可以不初始化值,默认从0自增
// MALE,
// FEMALE,
// UNKNOW
// }
enum Sex {
MALE = 1, // 可以给定起始值,自动递增
FEMALE,
UNKNOW
}
function checkSex(sex: Sex) {
let result = '';
switch(sex) {
case Sex.MALE:
result = '男';
break;
case Sex.FEMALE:
result = '女';
break;
case Sex.UNKNOW:
result = '未知';
break;
default:
break;
}
return result;
}
console.log(checkSex(1)) // '男'
TypeScript的泛型
泛型的概念
如果希望实现函数的返回值类型与参数类型一致,可以这样写:
function fun(arg: string): string {
return arg
}
或者这样:
function fun(arg: number): number {
return arg
}
但是这些写法都太局限,不能满足所有情况,这时就可以使用泛型,
泛型就是泛指的类型,用<>进行定义,一般习惯用单个大写字母例如<T>
function fun<T>(arg: T): T {
return arg // 即可实现返回值类型与参数类型一致
}
泛型数组
泛型数组的两种写法
// T[] 形式
function fun<T>(arg: T[]): T[] {
return arg.reverse()
}
fun<string>(['a', 'b', 'c', 'd'])
// fun(['a', 'b', 'c', 'd']) 泛型也支持类型推断
// Array<T>
function func<T>(arg: Array<T>): Array<T> {
return arg.reverse()
}
func<number>([1, 2, 3, 4, 5])
多个泛型定义
泛型可以定义多个,用法和一个类似
function fun<T, P>(first: T, second: P): [T, P] {
return [first, second]
}
fun<string, number>('string', 123)
泛型中的继承
泛型可以通过继承来进行约束
interface Key {
key: number
}
function fun<T extends Key>(arg: T): T { // 泛型T继承接口Key,所以参数必须含有key属性
return {
...arg,
key: new Date().getTime() + arg.key
}
}
fun({
key: 123,
name: '测试'
})
泛型的简单应用
实现一个 axios 方法,接收 url 和 payload 两个参数,根据 url 的不同,接收参数类型及返回值类型也不同
interface Todo {
// Todo类型接口
id: number;
name: string;
done: boolean;
}
let todos: Todo[] = [
// 初始数据
{
id: 1,
name: "代办1",
done: false,
},
{
id: 2,
name: "代办2",
done: true,
},
];
enum Urls {
// 获取todo数据的接口,不需要参数,返回值类型为 Todo[]
TODOS = "/api/todos",
// 切换todo状态的接口,参数类型为 number,返回值类型 boolean
TOGGLE = "/api/toggle",
// 添加todo项的接口,参数类型为 Todo,返回值类型 boolean
ADD = "/api/add",
}
// 定义一个工具类型
// 根据泛型传入值来返回一个自定义的key
type Key<U> = U extends Urls.TODOS ? "todos" :
U extends Urls.TOGGLE ? "toggle" :
U extends Urls.ADD ? "add" : "other";
// 通过工具类型Key获取对应key,再根据key获取对应的参数类型
type Payload<P> = {
todos: any;
toggle: number;
add: Todo;
other: any;
}[Key<P>];
// 通过工具类型Key获取对应key,根据key获取对应的结果类型
type Result<R> = {
todos: Todo[];
toggle: boolean;
add: boolean;
other: any;
}[Key<R>];
function axios<T extends Urls>(
url: T,
payload?: Payload<T>
): Promise<Result<T>> | never {
let res;
switch (url) {
case Urls.TODOS: // 获取todo数据,不需要参数,返回值为Todo[]类型
res = todos.slice();
break;
case Urls.TOGGLE: // 改变todo状态,参数为number类型,返回值为boolean类型
const todo = todos.find(({ id }) => id === payload);
if (todo) {
todo.done = !todo.done;
}
res = true;
break;
case Urls.ADD: // 添加todo,参数为Todo类型,返回值为boolean类型
// 变量后面加! 为了让undefined和null通过编译
todos.push(payload!);
res = true;
break;
default:
throw new Error("Unknow api!");
}
return Promise.resolve(res as any);
}
axios(Urls.ADD, {
id: 3,
name: '做笔记',
done: false
}) // 添加一条数据
console.log(axios(Urls.TODOS)) // 获取全部数据
axios(Urls.TOGGLE, 2) // 切换 id为2 的数据的 done 状态