一、any/unknown/never
any:任意类型,不受任何约束,编译时会跳过类型检查,不推荐使用
unknown:可以理解为更安全的any,所有类型都可以分配给unknown,但是必须要在判断完它是什么类型之后才能继续使用
never:主要用于错误检查或抛出错误
1、unknown的使用
const unknownAge: unknown = 10;
// Error: Type 'unknown' is not assignable to type 'number'
const numberAge: number = unknownAge;
// 判断类型后使用
const age = typeof unknownAge === "number" ? unknownAge : 0;
function magicFunction(param: any) {
console.log(Math.round(param));
console.log(param.charAt(0));
console.log(param.push(1));
}
function magicFunction(param: unknown) {
// Error: Argument of type 'unknown' is not assignable to parameter of type 'number'
console.log(Math.round(param));
// Error: 'param' is of type 'unknown'
console.log(param.charAt(0));
// Error: 'param' is of type 'unknown'
console.log(param.push(1));
}
// 判断类型后使用
function magicFunction(param: unknown) {
if (typeof param === "number") {
console.log(Math.round(param));
} else if (typeof param === "string") {
console.log(param.charAt(0));
} else if (Array.isArray(param)) {
console.log(param.push(1));
} else {
throw new Error("Error");
}
}
2、never的使用
function fn(msg: string): never {
throw new Error(msg);
}
// type IconProps = ReactElement | { url: string; onError: () => void };
// 使用never避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码
// else分支编译会报错:Type 'string' is not assignable to type 'never'
type IconProps = ReactElement | { url: string; onError: () => void } | string;
const renderIcon = (icon: IconProps) => {
if (isValidElement(icon)) {
return icon;
} else if (typeof icon === "object") {
return <div>***</div>;
} else {
const exhaustiveCheck: never = icon;
throw new Error(`unknown type: ${exhaustiveCheck}`);
}
}
3、object和Object与{}
object:所有非原始类型,也就是说不能把string、boolean、number、bigint、symbol、null和undefined赋值给object
Object:拥有toString、hasOwnProperty方法的类型,所有原始类型、非原始类型都可以赋给Object。在严格模式下,null和undefined不能赋给Object
{}和Object一样
二、readonly
readonly只能标记基础类型(string number),Readonly<Type>用于标记对象和数组等
三、类型断言
1、非空断言 !
用于断言操作对象是非null和非undefined
let x: number;
initialize();
// Error: Variable 'x' is used before being assigned
// console.log(2 * x);
console.log(2 * x!);
function initialize() {
x = 10;
}
2、const断言
表示对象和数组里面的内容都是常量,不允许修改
const Status = {
success: 1,
error: 2,
};
Status.success = 20;
// const Status: { readonly success: 1; readonly error: 2; }
const Status = {
success: 1,
error: 2,
} as const;
// Error: Cannot assign to 'success' because it is a read-only property
Status.success = 20;
const arr = [1, 2, 3, 4];
arr[2] = 10;
// const arr: readonly [1, 2, 3, 4]
const arr = [1, 2, 3, 4] as const;
// Error: Cannot assign to '2' because it is a read-only property
arr[2] = 10;
四、类型操作
1、is关键字
2、in关键字
遍历类型
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
type Features = {
darkMode: () => void;
newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<Features>;
3、索引访问类型
数组用[number]访问,对象用["属性名"]访问
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
// 数组访问类型
type arr = (typeof MyArray)[number];
type arrs = ["Kars", "Esidisi", "Wamuu", "Santana"];
// type arrsKey = "Kars" | "Esidisi" | "Wamuu" | "Santana"
type arrsKey = arrs[number];
const tuple = ["tesla", "model 3", "model X", "model Y"] as const;
// 元组访问类型
// type tupleKey = "tesla" | "model 3" | "model X" | "model Y"
type tupleKey = (typeof tuple)[number];
type Person = {
name: string;
age: number;
};
// 对象访问类型
type age = Person["age"];
4、条件类型
type IType = "a" | "b" | "c";
type IType2 = "a" | "c";
// type Diff1 = "a" | "b" | "c" extends "a" | "c" ? never : "a" | "b" | "c"
// type Diff1 = "a" | "b" | "c"
type Diff1 = IType extends IType2 ? never : IType;
5、分布式条件类型
针对extends左边的联合类型会被自动分发
(string | number) extends T ? A : B
// 等价于
(string extends T ? A : B) | (number extends T ? A : B)
(string | number | boolean) extends T ? A : B
// 等价于
(string extends T ? A : B) | (number extends T ? A : B) | (boolean extends T ? A : B)
type Diff2<T, U> = T extends U ? never : T;
// type T1 = Diff2<"a", "a" | "c"> | Diff2<"b", "a" | "c"> | Diff2<"c", "a" | "c">
// type T1 = "b"
type T1 = Diff2<"a" | "b" | "c", "a" | "c">;
条件类型与分布式条件类型的区别是否有泛型,条件类型没有泛型,分布式条件类型有泛型
6、非裸类型的分布式条件类型
type Diff3<T, U> = [T] extends [U] ? never : T;
// type T2 = ["a" | "b" | "c"] extends ["a" | "c"] ? never : "a" | "b" | "c"
// type T2 = "a" | "b" | "c"
type T2 = Diff3<"a" | "b" | "c", "a" | "c">;
分布式条件类型是有前提的。条件类型中待检查的类型(即extends左边的类型)必须是裸类型。即没有被诸如数组,元组或者函数包裹,没有对T进行取值操作
// 分布式条件类型
type GetSomeType<T extends string | number> = T extends string ? "a" : "b";
// let someTypeThree: string extends string ? "a": "b" | number extends string ? "a": "b"
// let someTypeThree: "a" | "b"
let someTypeThree: GetSomeType<string | number>;
// 非裸类型的分布式条件类型
type GetSomeTypes<T extends string | number> = [T] extends string ? "a" : "b";
// let someTypeThrees: [string | number] extends string ? "a" : "b"
// let someTypeThrees: "b"
let someTypeThrees: GetSomeTypes<string | number>;
// 非裸类型的分布式条件类型
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
// type StrArrOrNumArr = [string | number] extends [any] ? (string | number)[] : never;
// type StrArrOrNumArr = (string | number)[]
type StrArrOrNumArr = ToArrayNonDist<string | number>;
interface Cat {
type: "cat";
breeds: "Abyssinian" | "Shorthair" | "Curl" | "Bengal";
}
interface Dog {
type: "dog";
breeds: "Hound" | "Brittany" | "Bulldog" | "Boxer";
color: "brown" | "white" | "black";
}
// 非裸类型的分布式条件类型
type LookUp<T extends { type: string }, U extends string> = T["type"] extends U
? T
: never;
// type MyDog = "cat" | "dog" extends "dog" ? Cat | Dog : never
// type MyDog = never
type MyDog = LookUp<Cat | Dog, "dog">;
// 分布式条件类型
type LookUp1<T extends { type: string }, U extends string> = T extends unknown
? T["type"] extends U
? T
: never
: never;
// type MyDog1 = Dog
type MyDog1 = LookUp1<Cat | Dog, "dog">;
7、交叉类型 &
如果是基本类型,则取交集,如果是对象,则取对象的属性交集
// type User = never
type User = string & number;
// 没有同名属性交叉
// type IntersectionType = { id: number; name: string; age: number }
type IntersectionType = { id: number; name: string } & { age: number };
const mixed: IntersectionType = {
id: 1,
name: "name",
age: 18,
};
// 有同名属性,且属性为基本类型
// type IntersectionTypeConfict = { id: number; name: never; age: number }
type IntersectionTypeConfict = { id: number; name: string } & {
age: number;
name: number;
};
const mixedConflict: IntersectionTypeConfict = {
id: 1,
// Type 'number' is not assignable to type 'never'
name: 2,
age: 2,
};
// 有同名属性,且属性有交集
// type IntersectionTypeConfict = { id: number; name: 2; age: number }
type IntersectionTypeConfict = { id: number; name: 2 } & {
age: number;
name: number;
};
let mixedConflict: IntersectionTypeConfict = {
id: 1,
name: 2,
age: 2,
};
mixedConflict = {
id: 1,
// Type '22' is not assignable to type '2'
name: 22,
age: 2,
};
interface A {
x: { d: true };
}
interface B {
x: { e: string };
}
interface C {
x: { f: number };
}
// 有同名属性,且属性为对象
// type ABC = { x: { d: true; e: string; f: number } }
type ABC = A & B & C;
let abc: ABC = {
x: {
d: true,
e: "",
f: 666,
},
};
8、数字分隔符_
let num:number = 1_2_345.6_78_9
// 编译成JS
"use strict";
let num = 12345.6789;
五、infer
infer代表待推断类型,必须和extends条件约束类型一起使用
// 函数参数类型推断为P
type ParamType<T> = T extends (...args: infer P) => any ? P : T;
type GetAge = (name: string, age: number) => void;
// type AgeParamType = [name: string, age: number]
type AgeParamType = ParamType<GetAge>;
// 数组中的元素类型推断为Item
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
// 函数返回值的类型推断为Return
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
? Return
: never;
// type Num = number
type Num = GetReturnType<() => number>;
// type Str = string
type Str = GetReturnType<(x: string) => string>;
六、extends
如果是联合类型使用extends,会把每个类型拆分出来单独比较,如果是对象使用extends,会把对象作为一个整体
type Exclude<T, U> = T extends U ? never : T;
type string1 = "a" | "b" | "c";
type srring2 = "a" | "d";
// type str = "b" | "c"
type str = Exclude<string1, srring2>;
type obj1 = {
name: string;
age: number;
};
type obj2 = {
name: string;
sex: string;
};
// type obj = { name: string; age: number; }
type obj = Exclude<obj1, obj2>;
type obj3 = {
name: string;
age: number;
};
type obj4 = {
name: string;
};
// type obj5 = never
type obj5 = Exclude<obj3, obj4>;
七、元组
元组和数组的区别是:元组的数量和类型是确定的
let employee: [string, number];
// 类型必须匹配且数量必须为2
employee = ["hello", 10];
// 元组类型的可选元素
type Point = [number, number?, number?];
const x: Point = [10]; // 一维坐标点
const xy: Point = [10, 20]; // 二维坐标点
const xyz: Point = [10, 20, 10]; // 三维坐标点
// 元组类型的剩余元素
type RestTupleType = [number, ...string[]];
let restTuple: RestTupleType = [666, "Semlinker", "Kakuqo", "Lolo"];
console.log(restTuple[0], restTuple[1]);
// 只读的元组类型
const point: readonly [number, number] = [10, 20];
八、字面量类型
字面量不仅可以表示值,还可以表示类型,即所谓的字面量类型。3种类型:字符串字面量类型、数字字面量类型、布尔字面量类型,定义单个的字面量类型并没有太大的用处,主要应用场景是可以把多个字面量类型组合成一个联合类型
let specifiedStr: "this is string" = "this is string";
let specifiedNum: 1 = 1;
let specifiedBoolean: true = true;
interface Config {
size: "small" | "big";
isEnable: true | false;
margin: 0 | 2 | 4;
}
const定义的类型为字面量类型
// let str: string
let str = "this is string";
// const specifiedStr: "this is string"
const specifiedStr = "this is string";
九、接口与类型
官方推荐用interface,其他无法满足需求的情况下用type
1、相同点
都可以描述Object和Function
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void;
}
都可以被继承
// interface继承interface
interface Person {
name: string;
}
interface Student extends Person {
stuNo: number;
}
// interface继承type
type Person = {
name: string;
};
interface Student extends Person {
stuNo: number;
}
//type继承type
type Person = {
name: string;
};
type Student = Person & { stuNo: number };
//type继承interface
interface Person {
name: string;
}
type Student = Person & { stuNo: number };
类可以实现interface以及type(除联合类型外)
interface ICat {
setName(name: string): void;
}
class Cat implements ICat {
setName(name: string): void {
// todo
}
}
type ICat = {
setName(name: string): void;
};
class Cat implements ICat {
setName(name: string): void {
// todo
}
}
2、不同点
type可以定义基本类型,联合类型,元组
// 基本类型
type userName = string;
type stuNo = number;
// 联合类型
type Student = { stuNo: number } | { classId: number };
// 元组
type Data = [number, string];
多次声明一个同名的interface
interface Point { x: number; }
interface Point { y: number; }
const point: Point = { x: 1, y: 2 };
十、泛型
1、泛型常见字母
T:代表Type,定义泛型时通常用作第一个类型变量名称
K:代表Key,表示对象中的键类型;
V:代表Value,表示对象中的值类型;
E:代表Element,表示的元素类型;
十一、枚举
enum device {
phone,
notebook,
}
// 编译成JS
"use strict";
var device;
(function (device) {
device[device["phone"] = 0] = "phone";
device[device["notebook"] = 1] = "notebook";
})(device || (device = {}));
enum deviceStr {
phone = "1",
notebook = "2",
}
// 编译成JS
"use strict";
var deviceStr;
(function (deviceStr) {
deviceStr["phone"] = "1";
deviceStr["notebook"] = "2";
})(deviceStr || (deviceStr = {}));
const枚举,会在编译阶段删除该对象,且不能访问该枚举对象(对象被删除啦),只能访问该枚举对象成员,慎用
const enum obj {
A = 1,
B = "18",
}
// 编译成JS为空
// Error:'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.
console.log(obj);
console.log(obj.A, obj.B);
十二、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 // 为装饰器提供元数据的支持
}
十三、参考资料
3.2021TypeScript史上最强学习入门文章(2w字)
4.TypeScript中interface和type的区别,你真的懂了吗