# TypeScript
# 数据类型
null、number、undefined、string、boolean、symbol、bigint、object
数组、元组、any、unknown、never、枚举
注意:
- null和undefined可以赋值给任何类型。(除了严格模式下的Object和object)
never
表示的是那些永不存在的值的类型
- 函数运行时抛出异常
- 死循环
unknown
unknown和any一样,所有类型都可以分配给它
let a: unknown = null
a = 123
a = true
unkonw和any的区别
- 任何类型的值可以赋值给any,any也可以赋值给任何类型。
- 任何类型可以赋值给unknown,unknown只能赋值给unknown和any。
字面量类型
TypeScript 支持 3 种字面量类型:字符串字面量类型、数字字面量类型、布尔字面量类型。
interface Config {
size: 'small' | 'big';
isEnable: true | false;
margin: 0 | 2 | 4;
}
# 类型断言
手动指定一个值的类型
- 值 as 类型
- <类型>值
const断言
不让TS拓展它的类型,判断为最窄的类型
// Type is { x: number; y: number; }
const obj1 = {
x: 1,
y: 2
};
// Type is { x: 1; y: number; }
const obj2 = {
x: 1 as const,
y: 2,
};
// Type is { readonly x: 1; readonly y: 2; }
const obj3 = {
x: 1,
y: 2
} as const
# 接口&&类型别名
# 鸭式辨形法
在参数里面直接写对象有严格的类型定义,所以不能多参或者少参。而将参数对象赋值给变量,然后再由变量去传入参数时不会警告额外的属性检查。(即可以多属性,不能少属性。)
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj) // Ok
printLabel({ size: 10, label: "Size 10 Object" }); // Error
# type和interface区别
同:
- 两者都可以用来描述对象或函数的类型,但是语法不同。
- 都允许拓展(extends),但是方式不同。
异:
与接口不同,类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。
接口可以定义多次,类型别名不可以。接口定义多次会自动合并为单个接口。
接口的扩展就是继承,通过extends来实现。类型别名的扩展就是交叉类型,通过&来实现。
// 接口继承接口 interface PointX { x: number } interface Point extends PointX { y: number } // 类型别名继承类型别名 type PointX = { x: number } type Point = PointX & { y: number } // 接口继承类型别名 type PointX = { x: number } interface Point extends PointX { y: number } // 类型别名继承接口 interface PointX { x: number } type Point = PointX & { y: number }
# 联合类型和交叉类型
- 联合类型:某个变量可能是多个 interface 中的其中一个,用
|分割。 - 交叉类型:由多个类型组成,用
&连接
同名属性不兼容(类型不一样),交叉后会变成never类型。
同名属性,比如一个是 number,另一个是number的子类型合并后是属性的子类型。
type IntersectionTypeConfict = { id: number; name: 2; } & { age: number; name: number; }; let mixedConflict: IntersectionTypeConfict = { id: 1, name: 2, // ok age: 2 }; mixedConflict = { id: 1, name: 22, // '22' 类型不能赋给 '2' 类型 age: 2 };如果同名属性是非基本类型,则可以合并成功。
interface A { x:{d:true}, } interface B { x:{e:string}, } interface C { x:{f:number}, } type ABC = A & B & C let abc:ABC = { x:{ d:true, e:'', f:666 } }
# 类型守卫
# keyof
用于获取一个接口中 key 的联合类型
interface Animal {
name: string
age: number
}
type i = keyof Animal
// 等同于
type i = 'name' | 'age'
// 用法二:
type Todo = {
id: number;
text: string;
done: boolean;
}
const todo: Todo = {
id: 1,
text: "Learn TypeScript keyof",
done: false
}
function prop<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
# typeof
获取一个变量或对象的类型
const xiaoming: Animal & Person = undefined
type me = typeof xiaoming // me的类型为Animal & Person
# in
遍历枚举类型
用法一:
type Keys = 'option1' | 'option2';
type Flags = {
[K in Keys]: boolean
}
// 等价于
type Flags = {
option1: boolean
option2: boolean
}
用法二:
function f() {
if ('name' in xiaoming) {
return 'name'
}
}
# 泛型
# 泛型约束
一般通过extend对泛型进行约束
interface Sizeable {
size: number;
}
function trace<T extends Sizeable>(arg: T): T {
console.log(arg.size);
return arg;
}
function getValues<T, K extends keyof T>(person: T, keys: K[]): T[K][] {
return keys.map(key => person[key]);
}
interface Person {
name: string;
age: number;
}
const person: Person = {
name: 'Fendy',
age: 16
}
# 函数重载
函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。
type Types = number | string
function add(a:number,b:number):number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a:Types, b:Types) {
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString();
}
return a + b;
}
const result = add('Semlinker', ' Kakuqo');
result.split(' ')
# 工具类型
# Partial
将T中所有属性转换为可选属性,返回的类型可以是T的任意子集.
interface UserModel {
name: string
age?: number
sex: number
}
type JUserModel = Partial<UserModel>
// 相当于
type JUserModel = {
name?: string
age?: number
sex?: number
}
# Required
将T的所有属性设置为必选属性来构造一个新的类型,与Partial相反。
# Readonly
将T中所有属性设置为只读。
# Record<K,T>
将K中所有属性设置为T类型
type TodoProperty = 'title' | 'description';
type Todo = Record<TodoProperty, string>;
// =
type Todo = {
title: string;
description: string;
}
interface IGirl {
name: string;
age: number;
}
type allGirls = Record<string, IGirl>
# Pick<T,K>
在一个声明好的类型中,挑选一部分出来组成一个新的类型
interface Todo {
title: string;
description: string;
done: boolean;
}
type TodoBase = Pick<Todo, "title" | "done">;
// =
type TodoBase = {
title: string;
done: boolean;
}
# Omit<T,K>
从T中取出除去K的其他所有类型,与Pick相反
# Exclude<T,U>
从T中排除可分配给U的属性,剩余的属性构成新的类型
# Extract<T,U>
从T中抽出可分配给U的属性构成新的类型。与Exclude相反
# 常量枚举和枚举
const枚举会在ts编译期间被删除,避免额外的性能开销
枚举在编译时会被编译成一个对象,可以被当作对象使用
// const枚举
const enum Witcher {
Ciri = 'Queen',
Geralt = 'Geralt of Rivia'
}
// 普通枚举
enum Witcher {
Ciri = 'Queen',
Geralt = 'Geralt of Rivia'
}
# const和readonly
# object、Object、{}
# object
表示所有非原始类型,也就是说我们不能把number、string、boolean、symbol等原始类型赋值给object。
# Object && {}
原始类型可以赋值给Object或者{}
let lowerCaseObject: object;
lowerCaseObject = 1; // ts(2322)
lowerCaseObject = 'a'; // ts(2322)
lowerCaseObject = true; // ts(2322)
lowerCaseObject = null; // ts(2322)
lowerCaseObject = undefined; // ts(2322)
lowerCaseObject = {}; // ok
let upperCaseObject: Object;
upperCaseObject = 1; // ok
upperCaseObject = 'a'; // ok
upperCaseObject = true; // ok
upperCaseObject = null; // ts(2322)
upperCaseObject = undefined; // ts(2322)
upperCaseObject = {}; // ok
# 总结
{}、Object是比object更宽泛的类型,{}和Object可以互相代替,用来表示原始类型(null、undefined 除外)和非原始类型;而object则表示非原始类型。
# 绕开额外属性检查的方法
# 鸭式辨形法
# 类型断言
interface Props {
name: string;
age: number;
money?: number;
}
let p: Props = {
name: "兔神",
age: 25,
money: -100000,
girl: false
} as Props; // OK
# 索引签名
interface Props {
name: string;
age: number;
money?: number;
[key: string]: any;
}
let p: Props = {
name: "兔神",
age: 25,
money: -100000,
girl: false
}; // OK
# tsconfig.json
重要字段
files - 设置要编译的文件的名称; include - 设置需要进行编译的文件,支持路径模式匹配; exclude - 设置无需进行编译的文件,支持路径模式匹配; compilerOptions - 设置与编译流程相关的选项。
"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 // 为装饰器提供元数据的支持
}