# Typescript 笔记
# TS 是什么
Typescript (opens new window)(以下简称 TS)是基于 javscript 语言的一种超集,本质上向这种语言加入了可选静态类型和基于类的面向对象编程。
TS 和 ECMAScript 之间的关系图如下图:
# TS 和 JS 的区别
TS | JS |
---|---|
JavaScript 的超集用于解决大型项目的代码复杂性 | 一种脚本语言,用于创建动态网页 |
在编译期间发现并纠正错误 | 作为一种解释性语言,只能在运行时发现错误 |
强类型,支持动态和静态类型 | 弱类型,无静态类型 |
最终变异成 JS,可被浏览器识别 | 可直接运行在浏览器 |
支持模块、泛型、接口 | 不支持 |
生态不是很大 | 社区庞大 |
# 安装并使用
1.安装 Typescript
npm install -g typescript
# yarn
yarn global add typescript
2.编译 Typescript
创建 test.ts
const a: String = "12";
执行编译,目录会增加编译的 JS 文件
tsc test
var a = "12";
# TS 工作流程
通常 TS 代码在编译后都会进行一个打包处理,然后进行部署。
# TS 基本数据类型
# Boolean
let isDone: boolean = false;
// ES5:var isDone = false;
# Number
let count: number = 10;
// ES5:var count = 10;
# String
let name: string = "semliker";
// ES5:var name = 'semlinker';
# Symbol
const sym = Symbol();
let obj = {
[sym]: "semlinker"
};
console.log(obj[sym]); // semlinker
# Array
// 1. 直接定义
let list: number[] = [1, 2, 3];
// 2. 定义泛型
let list: Array<number> = [1, 2, 3];
// 3. 定义接口
interface NumberArray {
[index: number]: number;
}
let list: NumberArray = [1, 2, 3];
// 类数组
function sum() {
let args: IArguments = arguments;
}
// 任意类型数组
let list: any[] = ["xcatliu", 25, { website: "http://xcatliu.com" }];
// 定义对象数组
const arr: { age: string }[] = [{ age: "dell" }];
// 规定某种对象格式
type User = {
name: string;
age: number;
};
const arr3: User[] = [{ name: "12", age: 12 }];
// 甚至可以定义一个类对象数组
class Teacher {
name: string;
age: number;
}
const arr4: Teacher[] = [new Teacher(), { name: "12", age: 12 }];
// 二位数组 + 元祖定义
const demo1: [string, number, string][] = [
["1", 2, "1"],
["2", 3, "3"]
];
// 解构数组
let arr: number[] = [0, 1, 2, 3];
let x: number;
let y: number;
let z: number;
[x, y, z] = arr;
# Enum
数字枚举
enum Direction { NORTH, SOUTH, EAST, WEST } let dir: Direction = Direction.NORTH; // 默认情况下,NORTH 的初始值伟0,其他成员递增
// 默认情况下编译上述 TS 得到的ES5代码 "use strict"; var Direction; (function(Direction) { Direction[(Direction["NORTH"] = 0)] = "NORTH"; Direction[(Direction["SOUTH"] = 1)] = "SOUTH"; Direction[(Direction["EAST"] = 2)] = "EAST"; Direction[(Direction["WEST"] = 3)] = "WEST"; })(Direction || (Direction = {})); var dir = Direction.NORTH;
字符串枚举
enum Direction { NORTH = "NORTH", SOUTH = "SOUTH", EAST = "EAST", WEST = "WEST" } let dirName = Direction[0]; // NORTH let dirVal = Direction["NORTH"]; // 0 这里会出现反向映射
"use strict"; var Direction; (function(Direction) { Direction["NORTH"] = "NORTH"; Direction["SOUTH"] = "SOUTH"; Direction["EAST"] = "EAST"; Direction["WEST"] = "WEST"; })(Direction || (Direction = {}));
常量枚举
添加
const
关键词即可异构枚举
字符串和数字混合组合
enum Enum { A, B, C = "C", D = "D", E = 8, F } console.log(Enum.A); //输出:0 console.log(Enum[0]); // 输出:A
# Any
普通类型在赋值过程中改变类型是不允许的
let myFavoriteNumber: string = "seven";
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
如果是 any
类型,则允许被赋值为任意类型
let myFavoriteNumber: any = "seven";
myFavoriteNumber = 7;
任意值得属性和方法访问都是被允许的*,也允许调用任何方法
声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
let anyThing: any = "hello";
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
# Unknown
和
any
相似,所有类型都可以赋值给unknown
let value: unknown; value = true; // OK value = 42; // OK value = "Hello World"; // OK value = []; // OK value = {}; // OK value = Math.random; // OK value = null; // OK value = undefined; // OK value = new TypeError(); // OK value = Symbol("type"); // OK
unknown
类型只能被赋值给any
和unknown
let value: unknown; let value1: unknown = value; // OK let value2: any = value; // OK let value3: boolean = value; // Error let value4: number = value; // Error let value5: string = value; // Error let value6: object = value; // Error let value7: any[] = value; // Error let value8: Function = value; // Error
unknown
类型的方法属性并不被允许访问let value: unknown; value.foo.bar; // Error value.trim(); // Error value(); // Error new value(); // Error value[0][1]; // Error
# Tuple
元组类型通常用来表示单个变量中存储不同类型的值,必须要类型匹配
let tupleType: [string, boolean];
tupleType = ["semlinker", true];
# Void
函数输出类型为任意类型
# Never
函数的永远不会结束执行,取到结果或者返回结果。
- 抛出异常
while
脚本
function test(): never {
throw new Error();
}
function test1(): never {
while (true) {}
}
# Null or Undefined
let u: undefined = undefined;
let n: null = null;
# Object
# 对象类型-接口
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
interface Person {
name: string;
age: number;
gender?: string; //可选类型,
readonly id: number; //只读类型
}
let tom: Person = {
name: "Tom",
age: 25
};
赋值的时候,变量的形状必须和接口的形状保持一致。
const getPersonName: (person: { name: string }) => string = person => person.name;
const setPersonName: (person: { name: string }, name: string) => void = (person, name) => {
person.name = name;
};
// 改造成 Interface
interface Person {
name: string;
}
const getPersonName1: (person: Person) => string = person => person.name;
// type 和 interface 的区别是 type可以去表示基础类型,接口只能表示对象
善用缓存变量的形式解决传参类型报错
interface Person {
name: string;
age?: number;
}
const getPersonName1: (person: Person) => string = person => person.name;
getPersonName1({
name: "dell",
age: 11
}); // 此时代码会报错,传入的是自定义格式的对象,TS 默认会进行强校验,删除可选属性
// 使用缓存对象
const person = {
name: "dell",
age: 11
};
getPersonName1(person); // 此时就不会报错
// 或者还有一种解决方案,添加接口任意属性
interface Person {
name: string;
age: number;
[propName: string]: any;
}
# 类型别名
type 和 interface 很相似,唯一区别就是 type可以定义联合类型,type可以用工具类,也就是高级操作符
type NumberOrString = string | number
# 函数类型
# 函数声明
function sum(x: number, y: number): number {
return x + y;
}
# 函数表达式
注意此处箭头不等于 ES6 的箭头,这里的箭头是函数的定义
在 TypeScript 的类型定义中,
=>
用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
let mySum = function(x: number, y: number): number {
return x + y;
};
// TS 箭头表达式写法
let mySum: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y;
};
# 接口定义函数
采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
interface searchFunc {
(x: number, y: number): number;
}
let fun: searchFunc = function(x: number, y: number): number {
return x + y;
};
# 可选参数
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + " " + lastName;
} else {
return firstName;
}
}
let tomcat = buildName("Tom", "Cat");
let tom = buildName("Tom");
可选参数必须接在必需参数后面
# 函数参数解构写法
function add({ first, second }: { first: number; second: number }) {
return first + second;
}
// 仅有一个参数
function add({ first }: { first: number }) {
return 2 * first;
}
# 参数默认值
function buildName(firstName: string, lastName: string = "Cat") {
return firstName + " " + lastName;
}
let tomcat = buildName("Tom", "Cat");
let tom = buildName("Tom");
# 重载
# 类型断言
如果你比Typescript
更了解某个值的详细信息,可以通过类型断言告诉编辑器。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。类型断言好比其他语言里的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。
1. 尖括号用法
let someValue: any = 'this is a string'
let strLength: number = (<string>someValue).length
2. as 语法
let someValue1: any = 'this is a string'
let strLength1: number = (someValue1 as string).length
interface bird {
fly: boolean;
sing: () => {};
}
interface dog {
fly: boolean;
bark: () => {};
}
// 类型断言 你比 TS 更加知道类型,因为TS在联合类型下只会去取公有属性
// as
function trainAnial(animal: bird | dog): void {
if (animal.fly) {
(animal as bird).sing();
} else {
(animal as dog).bark();
}
}
// in 判断属性名是否在该对象
function trainAnials(animal: bird | dog): void {
if ("sing" in animal) {
animal.sing();
} else {
animal.bark();
}
}
非空断言
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 !
可以用于断言操作对象是非 null 和非 undefined 类型。具体而言,x! 将从 x 值域中排除 null 和 undefined 。
原本 TS 会进行类型检查,加上非空断言符号,则放弃校验
//1.忽略 undefined 和 null 类型
function myFunc(maybeString: string | undefined | null) {
// Type 'string | null | undefined' is not assignable to type 'string'.
// Type 'undefined' is not assignable to type 'string'.
const onlyString: string = maybeString; // Error
const ignoreUndefinedAndNull: string = maybeString!; // Ok
}
const a: number | undefined = undefined;
const b: number = a!;
console.log(b); // undefined 忽略undefined类型
# 类型保护
// 类型保护 typeof instanceof
let addFirst: (num1: number | string, num2: number | string) => any = (num1, num2) => {
if (typeof num1 === "string" || typeof num2 === "string") {
return `${num1}${num2}`;
}
return num1 + num2;
};
# 操作符
# 键值取 keyof
使用
keyof
获取一个类型所有键值,发回一个联合类型type Person = { name: string; age: number; }; type PersonKey = keyof Person; // PersonKey得到的类型为 'name' | 'age'
keyof 的一个典型用途是限制访问对象的 key 合法化,因为 any 做索引是不被接受的
function getValue(p: Person, k: keyof Person) { return p[k]; // 如果k不如此定义,则无法以p[k]的代码格式通过编译 }
# typeof
高级用法
const me: Person = { name: "gzx", age: 16 };
type P = typeof me; // { name: string, age: number | undefined }
const you: typeof me = { name: "mabaoguo", age: 69 }; // 可以通过编译
和 JS
的 typeof
区分开,JS typeof在运行时执行,TS typeof 静态环境执行
export const useRequest = () => {
return typeof 1 === 'number' // 运行时执行
const { user } = useAuth();
return (...[url, config]: Parameters<typeof request>) => // TS静态的typeof,不会运行,最终编译的代码不会包含这一句
request(url, { ...config, token: user?.token });
};
# 遍历属性 in
[ 自定义变量名 in 枚举类型 ]: 类型
# 泛型
巧用泛型可以看VueUse
源码 (opens new window)
# 语法
当我们不知道传入的参数类型时,或者是一个泛指,此时就应该使用泛型
function identity<T, U>(value: T, message: U): T {
console.log(message);
return value;
}
console.log(identity<Number, string>(68, "Semlinker"));
# 泛型接口
// 声明一个函数
interface GenericIdentify<T> {
(arg: T): T;
}
# 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GGenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
return x + y;
};
# 泛型工具类型
# Tsconfig.json 配置
tsconfig.json
的作用
- 用于标识 TypeScript 项目的根路径;
- 用于配置 TypeScript 编译器;
- 用于指定编译的文件。
tsconfig.json
重要字段
files
- 设置要编译的文件的名称;include
- 设置需要进行编译的文件,支持路径模式匹配;exclude
- 设置无需进行编译的文件,支持路径模式匹配;compilerOptions
- 设置与编译流程相关的选项。
compilerOtions
选项
{
"compilerOptions": {
/* 基本选项 */
"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在编译中的库文件
"allowJs": true, // 允许编译 javascript 文件
"checkJs": true, // 报告 javascript 文件中的错误
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相应的 '.d.ts' 文件
"sourceMap": true, // 生成相应的 '.map' 文件
"outFile": "./", // 将输出文件合并为一个文件
"outDir": "./", // 指定输出目录
"rootDir": "./", // 用来控制输出目录结构 --outDir.
"removeComments": true, // 删除编译后的所有的注释
"noEmit": true, // 不生成输出文件
"importHelpers": true, // 从 tslib 导入辅助工具函数
"isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
/* 严格的类型检查选项 */
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
/* 额外的检查 */
"noUnusedLocals": true, // 有未使用的变量时,抛出错误
"noUnusedParameters": true, // 有未使用的参数时,抛出错误
"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
/* 模块解析选项 */
"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相对模块名称的基目录
"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
"typeRoots": [], // 包含类型声明的文件列表
"types": [], // 需要包含的类型声明文件名列表
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
/* Source Map Options */
"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
/* 其他选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
}
}
# 装饰器
装饰器是什么?
- 它是一个表达式
- 该表达式被执行后,返回一个函数
- 函数的入参分别为 target、name 和 descriptor
- 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
# 类装饰器
function Gretter(target: Function): void {
target.prototype.greet = function():void {
console.log('hello,类装饰器')
}
}
// 这里默认传递的参数就是 Gretting 类
@Gretter
class Gretting {
constructor() {
// 内部实现
}
let myGreeting = new Gretting();
(myGreeting as any).greet();
// 自定义输出问候语
function Greeter(greeting: string) {
return function (target: Function) {
target.prototype.greet = function (): void {
console.log(greeting);
};
};
}
@Greeter("Hello TS!")
class Greeting {
constructor() {
// 内部实现
}
}
let myGreeting = new Greeting();
(myGreeting as any).greet(); // console output: 'Hello TS!';
# 属性装饰器
function logProperty(target: any, key: string) {
delete target[key];
const backingField = "_" + key;
Object.defineProperty(target, backingField, {
writable: true,
enumerable: true,
configurable: true
});
// property getter
const getter = function(this: any) {
const currVal = this[backingField];
console.log(`Get: ${key} => ${currVal}`);
return currVal;
};
// property setter
const setter = function(this: any, newVal: any) {
console.log(`Set: ${key} => ${newVal}`);
this[backingField] = newVal;
};
// Create new property with getter and setter
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class Person {
@logProperty
public name: string;
constructor(name: string) {
this.name = name;
}
}
const p1 = new Person("semlinker");
p1.name = "kakuqo";
// 输出
Set: name => semlinker;
Set: name => kakuqo;
# 方法装饰器
function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log("wrapped function: before invoking " + propertyKey);
let result = originalMethod.apply(this, args);
console.log("wrapped function: after invoking " + propertyKey);
return result;
};
}
class Task {
@log
runTask(arg: any): any {
console.log("runTask invoked, args: " + arg);
return "finished";
}
}
let task = new Task();
let result = task.runTask("learn ts");
console.log("result: " + result);
打印结果
"wrapped function: before invoking runTask"
"runTask invoked, args: learn ts"
"wrapped function: after invoking runTask"
"result: finished"
# 参数装饰器
function Log(target: Function, key: string, parameterIndex: number) {
let functionLogged = key || target.prototype.constructor.name;
console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
been decorated`);
}
class Greeter {
greeting: string;
constructor(@Log phrase: string) {
this.greeting = phrase;
}
}
"The parameter in position 0 at Greeter has been decorated"
# 内置对象
let b: Boolean = new Boolean(1);
let e: Error = new Error("Error occurred");
let d: Date = new Date();
let r: RegExp = /[a-z]/;
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll("div");
document.addEventListener("click", function(e: MouseEvent) {
// Do something
});
# 声明文件
# 高级进阶
# keyof
索引查询:对应任何类型T
,keyof T
的结果为该类型上所有共有属性key的联合:
interface Eg1 {
name: string,
readonly age: number,
}
// T1的类型实则是name | age
type T1 = keyof Eg1
class Eg2 {
private name: string;
public readonly age: number;
protected home: string;
}
// T2实则被约束为 age
// 而name和home不是公有属性,所以不能被keyof获取到
type T2 = keyof Eg2
T[K]
索引访问
interface Eg1 {
name: string,
readonly age: number,
}
// string
type V1 = Eg1['name']
// string | number
type V2 = Eg1['name' | 'age']
// any
type V2 = Eg1['name' | 'age2222']
// string | number
type V3 = Eg1[keyof Eg1]
# partial
Partial<T>
将T
的所有属性变成可选的。
/**
* 主要通过K extends keyof T约束K必须为keyof T的子类型
* keyof T得到的是T的所有key组成的联合类型
*/
type PartialOptional<T, K extends keyof T> = {
[P in K]?: T[P];
}
/**
* @example
* type Eg1 = { key1?: string; key2?: number }
*/
type Eg1 = PartialOptional<{
key1: string,
key2: number,
key3: ''
}, 'key1' | 'key2'>;
# ReturnType
返回函数的返回值类型
export type UseBroswerLocationReturn = ReturnType<typeof useBrowserLocation>;
# Paramters
传入泛型函数类型,返回函数的参数类型
// 传入request 函数类型,然后用Paramters获取函数参数类型,然后当成接口去定义另一个函数的参数
return (...[url, config]: Parameters<typeof request>) =>
request(url, { ...config, token: user?.token });
# Utilify type
Utilify type
的作用就是传入泛型类型,然后对这个类型进行某种操作
常见的 Utilify Type
example中出现两次定义,后者代表解答或者类型
# Paramters
提取T类型的属性,作为一个新的类型
declare function f1(arg: { a: number; b: string }): void;
type T0 = Parameters<() => string>;
type T0 = []
type T1 = Parameters<(s: string) => void>;
type T1 = [s: string]
type T2 = Parameters<<T>(arg: T) => T>;
type T2 = [arg: unknown]
type T3 = Parameters<typeof f1>;
type T3 = [arg: {
a: number;
b: string;
}]
type T4 = Parameters<any>;
type T4 = unknown[]
type T5 = Parameters<never>;
type T5 = never
type T6 = Parameters<string>;
Type 'string' does not satisfy the constraint '(...args: any) => any'.
type T6 = never
type T7 = Parameters<Function>;
Type 'Function' does not satisfy the constraint '(...args: any) => any'.
Type 'Function' provides no match for the signature '(...args: any): any'.
type T7 = never
# Partial
所有属性变为可选
/**
* 主要通过K extends keyof T约束K必须为keyof T的子类型
* keyof T得到的是T的所有key组成的联合类型
*/
type PartialOptional<T, K extends keyof T> = {
[P in K]?: T[P];
}
/**
* @example
* type Eg1 = { key1?: string; key2?: number }
*/
type Eg1 = PartialOptional<{
key1: string,
key2: number,
key3: ''
}, 'key1' | 'key2'>;
# Required
所有属性变为必选
interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 };
const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.
# Readonly
只读所有属性
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: "Delete inactive users",
};
todo.title = "Hello";
// Cannot assign to 'title' because it is a read-only property.
# Record<K,T>
构造一个类型,该类型具有一组属性K,每个属性的类型为T。可用于将一个类型的属性映射为另一个类型。可以理解为定义一个限制key
,value
类型的新类型
type TodoProperty = "title" | "description";
type Todo = Record<TodoProperty, string>;
const todo: Todo = {
title: '12',
description: '12'
};
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};
cats.boris;
# Pick<T,K>
通过在T中抽取一组属性K构建一个新类型.
interface Todo {
title: string;
description: string;
done: boolean;
}
type TodoBase = Pick<Todo, 'title' | 'done'>;
const todo: TodoBase = {
title: 'First Todo',
done: false
};
# Omit<T,K>
通过在T中抽取除去属性K,构建一个新类型
interface Todo {
title: string;
description: string;
done: boolean;
}
type TodoBase = Omit<Todo, 'description'>;
const todo: TodoBase = {
title: 'First Todo',
done: false
};
# Exclude<T,U>
从T中排除可分配给U的属性,剩余的属性构成新的类型
type T0 = Exclude<string | number | boolean, boolean > // string | number
type T0 = Exclude<"a" | "b" | "c", "a">;
type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
type T2 = string | number
# Extract<T,U>
从 T 中取出可以分配给 U 的属性,组成新的类型
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
type T1 = () => void
# ReturnType
T函数类型的返回类型
declare function f1(): { a: number; b: string };
type T0 = ReturnType<() => string>;
type T0 = string
type T1 = ReturnType<(s: string) => void>;
type T1 = void
type T2 = ReturnType<<T>() => T>;
type T2 = unknown
type T3 = ReturnType<<T extends U, U extends number[]>() => T>;
type T3 = number[]
type T4 = ReturnType<typeof f1>;
type T4 = {
a: number;
b: string;
}
type T5 = ReturnType<any>;
type T5 = any
type T6 = ReturnType<never>;
type T6 = never
# TS 解决空值或者undefined问题
loadsh
解决// data.courseInfo.description 完美解决null或者undefined问题 let a = _.get(data,'courseInfo.description','this is a description') //第三个参数是默认值,也就是当路径存在undefined就会返回这个默认值
可选操作符
?
# TS重载技巧
例: useAxios(url,config,instance)
支持传入三个参数,其中第二个和第三个参数可选
那么定义函数就可以先定义方法重载,包含所有情况
export function useAxios<T = any>(url: string, config?: AxiosRequestConfig): UseAxiosReturn<T>
export function useAxios<T = any>(url: string, instance?: AxiosInstance): UseAxiosReturn<T>
export function useAxios<T = any>(url: string, config: AxiosRequestConfig, instance: AxiosInstance): UseAxiosReturn<T>
// 真正实现该函数
export function useAxios<T = any>(url: string, ...args: any[]) {
}