TS编译运行
ts不是在终端运行,是一门中间语言,最终编译为js运行。
手动编译
// 1. ts编译为js
npm i -g typescript
// 查看版本
tsc -v
// 编译运行
tsc .\test.ts
// 2. ts直接在node环境下运行
npm i -g ts-node
// 查看版本
ts-node -v
// 编译运行
ts-node .\test.ts
webpack构建ts项目
安装
# 生成package.json文件
npm init -y
# 安装webpack环境
npm i -D webpack webpack-cli
# 安装ts解析
npm i -D ts-loader
# 安装html模板
npm i -D html-webpack-plugin
# 安装webpack本地服务器,热更新
npm i -D webpack-dev-server
# 简写
npm i -D webpack webpack-cli ts-loader html-webpack-plugin webpack-dev-server
webpack.config.js
在根目录下编写webpack.config.js文件
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
resolve: {
extensions: [".ts", ".js", ".cjs", ".json"],
},
devServer: {
port: 1988,
proxy: {},
},
module: {
rules: [
{
test: /\.ts$/,
loader: "ts-loader",
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/resource",
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
};
tsconfig.json
生成ts配置文件,tsconfig.json
tsc --init
1. tsconfig.json文件所在目录即为TS项目的根目录
2. 指定该根目录下的ts文件编译方式
3. webpack中使用ts-loader打包时,自动读取tsconfig配置文件来编译ts代码
~~~json
"compilerOptions": {
"incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
"tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
"diagnostics": true, // 打印诊断信息
"target": "ES5", // 编译目标语言的版本
"module": "CommonJS", // 模块化使用的标准
"outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
"lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
"allowJS": true, // 允许编译器编译JS,JSX文件
"checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
"outDir": "./dist", // 指定输出目录
"rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
"declaration": true, // 生成声明文件,开启后会自动生成声明文件
"declarationDir": "./file", // 指定生成声明文件存放目录
"emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
"sourceMap": true, // 生成目标文件的sourceMap文件
"inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
"declarationMap": true, // 为声明文件生成sourceMap
"typeRoots": [], // 第三方声明文件的目录,默认时node_modules/@types
"types": [], // 加载的声明文件包
"removeComments":true, // 删除注释
"noEmit": true, // 不输出文件,即编译后不会生成任何js文件
"noEmitOnError": true, // 发送错误时不输出任何文件
"noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
"importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
"downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
"strict": true, // 开启所有严格的类型检查
"alwaysStrict": true, // 在代码中注入'use strict'
"noImplicitAny": true, // 不允许隐式的any类型
"strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
"strictFunctionTypes": true, // 不允许函数参数双向协变
"strictPropertyInitialization": true, // 类的实例属性必须初始化
"strictBindCallApply": true, // 严格的bind/call/apply检查
"noImplicitThis": true, // 不允许this有隐式的any类型
"noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
"noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
"noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
"noImplicitReturns": true, //每个分支都会有返回值
"esModuleInterop": true, // 允许export=导出,由import from 导入
"allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
"moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { // 路径映射,相对于baseUrl
// 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
"jquery": ["node_modules/jquery/dist/jquery.min.js"]
},
"rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
"listEmittedFiles": true, // 打印输出文件
"listFiles": true// 打印编译的文件(包括引用的声明文件)
}
// 指定一个匹配列表(编译指定路径下的所有ts相关文件)
"include": [
"src/**/*"
],
// 指定一个排除列表(不编译指定的ts文件)
"exclude": [
"demo.ts"
],
// 指定哪些文件使用该配置(编译指定文件,不能指定文件夹)
"files": [
"demo.ts"
]
基本类型
声明的类型称为类型注解(Type Annotation)
基本类型:
number、string、boolean、undefined、null、Symbol、BigInt
// 非引用类型:
// number、string、boolean、undefined、null、Symbol、BigInt
let num:number = 10
let num2:number = 0b110
let num3:number = NaN
let num4:number = Infinity
let flag:boolean = false
let str:string = '123'
let str2:string = "abc"
let unde:undefined = undefined
let nu:null = null
// 表示唯一,即使内容一样也表示唯一
let s1:symbol = Symbol('k1')
let s2:symbol = Symbol('k1')
let person = {
[k1]:'v1',
[k1]:'v2'
}
// 字面量类型
var ENV:"production"|"development"|"test"='development'
let age:99|100 = 99
枚举类型
enum nums {
one = 1,
two,
three
}
// 从1开始递增
// 3
console.log(nums.three);
// 通过名称拿到值
// 也能从值拿到名称
// { '1': 'one', '2': 'two', '3': 'three', one: 1, two: 2, three: 3 }
console.log(nums);
引用类型
引用类型:object、array、function
数组
// 只能空数组
let arr1:[] = []
let arr2:number[] = [1,2,3]
let arr3:Array<number> = [1,2,3]
// 二维数组
let arr4:number[][]=[[1],[2]]
let arr5:Array<Array<number>>=[[1],[2]]
// 数组内有字符串和数字类型
var arr6:(string|number)[] = ['cjc',999]
// 字符串数组或数字数组
var arr7: string[] | number[] = ['a', 'b']
var arr8: string[] | number[] = [1, 2]
// 元祖,顺序和类型都要满足
// 最后任意类型且可有可无
var arr9: [string, number, any?] = ['cjc', 999, true]
// 二维数组
var arr10: [number,string][] = [
[0,'a'],
[1,'b']
]
// 不定参数
var arr11: [string, number, ...(number|string)[]] = ['cjc', 999, 1,2,3]
对象
// 空对象
let obj:object = {}
let obj2:{} = {name:'cjc'}
let obj3:{name:string,age:number} = {name:'cjc',age:999}
// 键名数字类型
var obj4: { [propNmae: number]: string } = { 1: 'a', 2: 'b' }
// 可选 ?
var obj5: { x: number, y?: boolean } = { x: 1 }
函数
函数定义方式
// 1.声明式函数定义
function sumFun(a: number, ...args: number[]): number {
return a + args.reduce((preVal: number, curVal: number) => {
return preVal + curVal
})
}
console.log(sumFun(1, 2, 3)); //6
// 2.函数变量
let foo2: (a: string) => string = function (a) {
return a
}
// 3.匿名函数
// 一般匿名函数不用指定类型
callback(function(){})
// this处理
function foo3(this:void,a:number){}
foo3(1)
函数类型
方式1 函数类型表达式
- 定义函数类型若有参数,则必须携带形参名
// 定义函数的类型
// 必须携带形参名
type calcFnType = (num1: number, num2: number) => number
function calc(calcFn: calcFnType) {
let res = calcFn(1, 2)
return res
}
function add(a: number, b: number) {
return a + b
}
function minus(a: number, b: number) {
return a - b
}
let res = calc(add)
// 3
console.log(res);
let res2 = calc(minus)
// -1
console.log(res2);
方式2 调用签名(call signatures)
函数本身也是对象类型,可以给函数指定类型的同时添加属性
- 定义函数类型若有参数,则必须携带形参名
- 函数类型语法 (形参名:类型) : 类型
interface IFoo {
name: string,
age: number,
(num: number): number
}
const foo: IFoo = (num: number): number => {
return 1
}
foo.name = 'ccc'
foo.age = 999
函数重载
函数重载:函数名相同,形参不同(个数、类型、顺序)
//输入数字 `123` 的时候,输出反转的数字 `321`,输入字符串 `'hello'` 的时候,输出反转的字符串 `'olleh'`
// 1.函数重载签名
function reverse(x: number): number;
function reverse(x: string): string;
// 2.通用函数实现
function reverse(x: any): any {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
any、unknown
any、unknown都能被赋予任何类型的值
any
- any能赋值给任何类型
- any类型能进行任何操作
unkonwn
- unknown 类型只能赋值给 any 类型和 unknown 类型本身
- unknown类型不能进行任何操作,类型缩小(例如typeof)后才能进行对应类型的操作
// 1.any
let a: any = 123
let b: string = a
// 2.unknown
// unknown比any更安全,unknown类型只能赋值给unknown或any
let a2: unknown = 123
//不能将类型“unknown”分配给类型“string”
// let b2:string = a2
let b2: any = a2
void
函数没有返回值时,返回void类型
type execFunType = (...args: any[]) => void
function delayFun(fn: execFunType) {
setTimeout(() => {
fn('ccc', 999)
}, 1000);
}
delayFun((name, age) => {
console.log(name);
console.log(age);
})
never
never类型表示永不存在的值的类型
应用场景:
- 函数中总是抛出异常或死循环(不会有返回值)
- 封装框架或工具时,确保所有类型都有对应的实现
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
没有对boolean类型的处理,报错:
增加对boolean的处理:
function foo(msg: string | number | boolean) {
switch (typeof msg) {
case 'string':
console.log(msg.length);
break;
case "number":
console.log(msg);
break;
case "boolean":
break;
default:
// 此时msg收窄为never
const check: never = msg
break;
}
}
tuple元祖
一般数组中存放相同的数据类型,元祖中可以存放不同的数据类型
const stu:[string,number] = ['CCC',999]
const name = stu[2]
应用场景:用于函数的返回值
function useState<T>(state: T): [T, (newState: T) => void] {
let currentState = state
const changeState = (newState: T) => {
currentState = newState
}
return [currentState, changeState]
}
//const counter: number
//const setState: (newState: number) => void
const [counter, setState] = useState(10)
// 10
console.log(counter);
字面量类型
function request(url: string, method: "get" | "post") { }
const info1 = {
url: 'xxx',
method: "get"
}
// info1中method的类型为string
// 方式1 断言为字面量"get"
request(info1.url, info1.method as "get")
// 方式2 将info对象类型断言为字面量类型
// 字面量推理
// const info2: { url: string, method: "get" | "post" } = {
// url: "xxx",
// method: "get"
// }
const info2 = {
url: "xxx",
method: "get"
} as const
request(info2.url, info2.method)
联合类型、交叉类型
联合类型
const a:number|string|boolean = true
交叉类型
interface IPerson {
name: string,
age: number
}
interface IStu extends IPerson {
name: string
study: () => void
}
// StuType类型具有IPerson和IStu的所有属性
type StuType = IPerson & IStu
const s1: StuType = {
name: 'ccc',
age: 999,
study() {
console.log(1);
}
}
JS内置类型
JS有ECMA、DOM、BOM组成
接下来介绍JS内置API相关的类型
// ECMA类型
let date: Date = new Date();
let reg: RegExp = new RegExp("/d/");
let err: Error = new Error();
let xhr: XMLHttpRequest = new XMLHttpRequest();
let local: Storage = localStorage;
let cookie: string = document.cookie;
let promise: Promise<number> = new Promise((resolve) => resolve(1));
// DOM元素类型
// 1.HTML[元素名]Element
// 2.HTMLElement
// 3.断言为Element
// 4.NodeListOf<DOM类型>
let div: HTMLDivElement | null = document.querySelector("div");
let h1: HTMLHeadingElement | null = document.querySelector("h1");
let header: HTMLElement | null = document.querySelector("header");
let span = document.querySelector("span") as Element;
let lis: NodeListOf<HTMLLIElement> = document.querySelectorAll("li");
type、interface
描述对象、函数
type类型别名和interface接口都可以描述对象和函数
type
// 描述对象
type IPoint = {
x: number
y: number
}
const p1: IPoint = { x: 1, y: 1 }
console.log(p1); //{ x: 1, y: 1 }
// 描述函数
type IGetPoint = (x: number, y: number) => ({ x: number, y: number })
const getPoint: IGetPoint = function (x: number, y: number): ({ x: number, y: number }) {
return ({ x, y })
}
const p2 = getPoint(2, 2)
console.log(p2); //{ x: 2, y: 2 }
interface
// 描述对象
interface IPoint {
x: number
y: number
}
const p1: IPoint = { x: 1, y: 1 }
console.log(p1); //{ x: 1, y: 1 }
// 描述函数
interface IGetPoint {
(x: number, y: number): ({ x: number, y: number })
}
const getPoint: IGetPoint = function (x: number, y: number): ({ x: number, y: number }) {
return ({ x, y })
}
const p2 = getPoint(2, 2)
console.log(p2); //{ x: 2, y: 2 }
type 还可以为基本类型起别名,interface不能
// primitive
type Name = string;
// union
type PartialPoint = PartialPointX | PartialPointY;
// tuple
type Data = [number, string];
可选、只读、索引签名
// 定义对象接口
interface Person {
// 1.只读属性
readonly id: number,
name: string,
age: number,
// 2.可选属性
gender?: string,
// 3.动态属性名
[attrName: string]: any
}
let p1: Person = {
id: 1,
name: 'cjc',
age: 999,
// 任意属性
song: 'jinitaimei'
}
索引签名
interface ICollection {
[index: number]: any
length: number
}
function printCollection(collection: ICollection) {
for (let i = 0; i < collection.length; i++) {
console.log(collection[i]);
}
}
const arr = [1, 2, 3]
printCollection(arr)
const str = 'abc'
printCollection(str)
类型扩展(继承)
接口可以扩展类型别名,而反过来是不行的
Interface extends interface
interface PartialPointX { x: number; }
interface Point extends PartialPointX {
y: number;
}
Type alias extends type alias
type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };
Interface extends type alias
type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }
Type alias extends interface
interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };
声明合并
同一个接口声明会合并
同一个类型别名会报错
interface pointType {
x: number,
y: number
}
interface pointType {
z?: number
}
const p1: pointType = {
x: 1,
y: 2,
}
const p2: pointType = {
x: 1,
y: 2,
z: 3
}
//{ x: 1, y: 2 } object
console.log(p1, typeof p1);
//{ x: 1, y: 2, z: 3 } object
console.log(p2, typeof p2);
类型断言、非空类型断言
当TS没法获取某个确定类型时,使用类型断言(Type Assertions)
// let div: HTMLDivElement | null
let div = document.querySelector('div')
// 当用类获取元素时,TS无法推断是什么类型
// let box1: Element | null
let box1 = document.querySelector('.box')
// let box2: HTMLDivElement
let box2 = document.querySelector('.box') as HTMLDivElement
断言能断言成更具体的类型或不太具体的类型
// const age: number
const age: number = 99
// const age2: any
const age2 = age as any
// const age3: string
const age3 = age2 as string
非空断言
interface IPerson {
name: string,
age: number,
hobbies?: string[]
}
const p1: IPerson = {
name: 'ccc',
age: 999,
hobbies: ['a']
}
// 访问属性 可选连 ?
console.log('设置之后:', p1.hobbies?.[0]);
// 设置属性
// 方式1 类型缩小
if (p1.hobbies) {
p1.hobbies[0] = 'rap'
}
// 方式2 非空类型断言
p1.hobbies![0] = 'rap'
console.log('设置之后:', p1.hobbies?.[0]);
类型缩小
- typeof
===
、!==
==
!=
常用于字面量类型的判断- instanceof
- in
TS面向对象
class语法
成员修饰符
- public 公有成员任何地方可见,默认值
- protected 在类内部及子类中可见
- private 私有成员,在类内部可见
属性修饰符
- readonly 只读属性
- ? 可选属性
interface IPerson {
name: string;
age: number;
}
class Person implements IPerson {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
type gradeItemType = {
math: number;
chinese: number;
};
class Student extends Person {
// 1.实例属性,实例方法
// 修饰符readonly、private、protected、public
gender: boolean;
private _secretMsg?: string;
constructor(name: string, age: number, gender: boolean) {
super(name, age);
this.gender = gender;
}
// 2.静态属性,静态方法
// 静态方法中this只能调用静态修饰符的属性和方法
static grade: gradeItemType[];
static getGrade() {
return this.grade;
}
// 3.get,set
get secretMsg() {
return "get secretMsg:" + this._secretMsg;
}
set secretMsg(newVal) {
this._secretMsg = "set secretMsg:" + newVal;
}
}
// 1.实例属性,方法
let stu1 = new Student("ccc", 999, true);
console.log(stu1.gender);
// 2.类调用静态方法,静态属性
Student.grade = [
{ math: 100, chinese: 100 },
{ math: 99, chinese: 99 },
];
console.log(Student.getGrade());
// 3.get,set
stu1.secretMsg = "哈哈哈";
console.log(stu1.secretMsg);
构造函数语法糖
class Person {
public name
public age
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
// 以上代码的语法糖形式:
class Person2 {
constructor(public name: string, public age: number) { }
}
泛型
基本使用
function foo<T, E>(a: T, b: E) { }
// 1.自动类型推断
foo(1, { name: 'ccc', age: 999 })
// 2. 类型注解
foo<number, { name: string, age: number }>(1, { name: 'ccc', age: 999 })
泛型约束
确保某属性存在(extends)
T extends U
- T 能够赋值给 U(T 是 U的子集)
例:传入的类型必须有length属性
//T必须有length属性,且length属性为number类型
function longer<T extends { length: number }>(a: T, b: T) {
return a.length > b.length ? a : b
}
// const longerArray: number[]
const longerArray = longer([1, 2], [1, 2, 3]);
// const longerString: "alice" | "bob"
const longerString = longer("alice", "bob");
// 类型“number”的参数不能赋给类型“{ length: number; }”的参数。
const notOK = longer(10, 100);
检查对象上的键是否存在(keyof)
例:根据对象的键(不管是否存在),获取对象的值
// function getProperty(obj: any, key: any): any
function getProperty(obj, key) {
return obj[key]
}
const p = {
name: 'ccc',
age: 99
}
// ccc
console.log(getProperty(p, "name"));
// 若没有类型约束,可以传入p中不存在的键
// undefined
console.log(getProperty(p, "gender"));
例:根据对象的键(只能获取对象p中存在的键),获取对象的值
keyof 获取p中所有的键组成的联合类型,如string|nunmber
// keyof使用
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
// function getProperty<O, K extends keyof O>(obj: O, key: K): O[K]
function getProperty<O, K extends keyof O>(obj: O, key: K) {
return obj[key]
}
const p = {
name: 'ccc',
age: 99
}
// ccc
console.log(getProperty(p, "name"));
// 类型“"gender"”的参数不能赋给类型“"name" | "age"”的参数
// console.log(getProperty(p, "gender"));
泛型默认类型
interface MyObject<T = string> {
id: T;
}
const numObject: MyObject = { id: "abc" };
const strObject: MyObject<number> = { id: 123 };
- 有默认类型的类型参数被认为是可选的。
- 必选的类型参数不能在可选的类型参数后。
映射类型
- 拷贝传入类型T的所有键值对到另一个类型
- 使用type定义映射类型,不能使用interface定义
- 拷贝过程中能对类型进行操作
interface IPerson {
name: string
age: number
}
type MapType<T> = {
// 拷贝传入类型T的所有键值对
[key in keyof T]: T[key]
}
type NewPerson = MapType<IPerson>
let p1: NewPerson = { name: 'ccc', age: 999 }
例子:将所有类型转为必填
- 在修饰符之前可以加上减号-,表示去除该修饰符
- 在修饰符之前可以加上加号+,默认省略
interface IPerson {
name: string
age?: number
readonly gender: string
}
type mapType<T> = {
// 拷贝传入类型T的所有键值对
// - 去除修饰符
-readonly [key in keyof T]-?: T[key]
}
type NewPerson = mapType<IPerson>
let p1: NewPerson = { name: 'ccc', age: 999, gender: 'male' }
ts模块化
没有export的js或ts文件会被认为是脚本。脚本中声明的变量和类型等在全局作用域中。
将脚本变为模块 export {} (即使没有导出任何内容)
导出类型
导入类型
ts声明文件
xxx.d.ts称为类型声明文件(Type Declaration)或类型定义文件(Type Definition)
ts内置类型声明文件
第三方库类型声明文件
- axios等第三方库包含自己编写的 xx.d.ts类型声明文件
- react等不含有类型声明文件,则需要另外导入类型声明库
npm i react
npm i -S @types/react
自定义类型声明文件
自定义类型声明文件 xxx.d.ts
// 声明变量类型
declare const name:string
declare const age:number
// 声明函数类型
declare function foo(a:number):number
// 声明类的类型
declare class Person {
constructor(public name:string,public age:number)
}
// 声明模块
// 假设lodash没有类型库
declare module "lodash" {
export function join(args: any[]):string;
}
// 声明命名空间
// jQuery等使用CDN方式使用时(不是引入模块的方式)
declare namespace $ {
export function ajax(setting:any):any
}
例:ts中导入图片文件,报错。需要声明文件模块
在类型声明文件中:
declare module "*.avif"
条件类型
条件类型在函数重载中应用
条件类型(Conditional Types)在函数重载中的应用
function sum(arg1: number, arg2: number): number
function sum(arg1: string, arg2: string): string
function sum(arg1, arg2) {
return arg1 + arg2
}
//function sum(arg1: number, arg2: number): number (+1 overload)
sum(1, 2)
//function sum(arg1: string, arg2: string): string (+1 overload)
sum('a', 'b')
function sum<T>(arg1: T, arg2: T): T extends number ? number : string
function sum(arg1, arg2) {
return arg1 + arg2
}
//function sum<number>(arg1: number, arg2: number): number
sum(1, 2)
//function sum<string>(arg1: string, arg2: string): string
sum('a', 'b')
在条件类型中推断类型(infer)
在条件类型中推断(Inferring Within Conditional Types)
- infer关键字只能在extends后面语句中使用
- infer推断的变量U只能在true分支中使用
例1:封装获取数组中元素类型的工具
// T是数组类型则返回数组中元素的类型
type UnpackedArray<T> = T extends (infer U)[] ? U : T
const arr1 = [1, 2, 3]
//type typeElem = number
type typeElem1 = UnpackedArray<typeof arr1>
const arr2 = ['a', 'b']
//type typeElem2 = string
type typeElem2 = UnpackedArray<typeof arr2>
例2:封装获取函数返回值类型的工具
type fnType = (arg1: number, arg2: number) => number
function foo() {
return "hi"
}
// 定义判断函数返回值类型的工具
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never
//type fnReturnType = number
type fnReturnType = MyReturnType<fnType>
// type fooReturnType = string
type fooReturnType = MyReturnType<typeof foo>
例3:封装获取函数参数类型的工具
type fnType = (arg1: number, arg2: number) => number
function foo() {
return "hi"
}
// 定义判断函数返回值类型的工具
type MyParamType<T extends (...args: any) => any> = T extends (...args: infer A) => any ? A : never
//type fnReturnType = [arg1: number, arg2: number]
type fnReturnType = MyParamType<fnType>
//type fooReturnType = []
type fooReturnType = MyParamType<typeof foo>
例4:获取数组第一个元素类型
type firstType<T> = T extends [infer oneType, ...any[]]
? oneType
: T;
分发条件类型
分发条件类型(Distributive Conditional Types)
在泛型中传入联合类型,联合类型的每一项会分发到条件类型中判断
type toArray<T> = T extends any ? T[] : never
//type newType = string[] | number[]
type newType = toArray<string | number>
内置工具类型
Partial
将类型对象的所有属性变为可选
interface IPerson {
name: string,
age: number,
gender?: boolean
}
// type IPersonOptional = {
// name?: string | undefined;
// age?: number | undefined;
// gender?: boolean | undefined;
// }
type IPersonOptional = Partial<IPerson>
自定义实现:
type MyPartial<T> = {
[k in keyof T]?: T[k]
}
Required
将类型对象的所有属性变为必选
interface IPerson {
name: string,
age?: number,
gender?: boolean
}
// type IPersonRequire = {
// name: string;
// age: number;
// gender: boolean;
// }
type IPersonRequire = Required<IPerson>
自定义实现:
type MyRequired<T> = {
[k in keyof T]-?: T[k]
}
Readonly
将类型对象的所有属性变为只读
interface IPerson {
name: string,
age?: number,
gender?: boolean
}
// type IPersonRequire = {
// readonly name: string;
// readonly age?: number | undefined;
// readonly gender?: boolean | undefined;
// }
type IPersonRequire = Readonly<IPerson>
自定义实现:
type MyReadonly<T> = {
readonly [k in keyof T]: T[k]
}
Record<Keys,Type>
构造一个类型对象,key为Keys类型,value为Type类型
interface IPerson {
name: string,
age?: number,
gender?: boolean
}
// 字面量类型
type no = "01" | "02"
// 构造一个类型对象,key为no类型,value为IPerson类型
type StusType = Record<no, IPerson>
const stus: StusType = {
"01": {
name: 'a'
},
"02": {
name: 'b'
}
}
type MyRecord<K extends keyof any, T> = {
[P in K]: T
}
Pick<Type,Keys>
构造一个类型,从Type类型中挑选一些属性Keys
interface IPerson {
name: string,
age?: number,
gender?: boolean
}
// type nameType = {
// name: string;
// }
type nameType = Pick<IPerson, 'name'>
// type nameAgeType = {
// name: string;
// age?: number | undefined;
// }
type nameAgeType = Pick<IPerson, 'name' | 'age'>
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
Omit<Type,Keys>
构造一个类型,从Type类型中删掉一些属性Keys
interface IPerson {
name: string,
age?: number,
gender?: boolean
}
// type nameType = {
// name: string;
// }
type nameType = Omit<IPerson, 'gender' | 'age'>
type MyOmit<T, K extends any> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
Exclude<T,U>
构造一个类型,从联合类型T中删掉一些属性U
type nums = 1 | 2 | 3
// type oneType = 1
type oneType = MyExclude<nums, 2 | 3>
type MyExclude<T, U> = T extends U ? never : T
Extract<T,U>
构造一个类型,从联合类型T中取出一些属性U
type nums = 1 | 2 | 3
//type oneType = 1
type oneType = Extract<nums, 1>
// Extract from T those types that are assignable to U
type MyExtract<T, U> = T extends U ? T : never
NonNullable
构造一个类型,删除T中所有null或undefined
// Exclude null and undefined from T
type MyNonNullable<T> = T extends null | undefined ? never : T
type nums = 1 | 2 | 3 | null | undefined
// type numsType = 1 | 2 | 3
type numsType = NonNullable<nums>
ReturnType
函数返回值类型
type fnType = (arg1: number, arg2: number) => number
function foo() {
return "hi"
}
// 定义判断函数返回值类型的工具
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never
//type fnReturnType = number
type fnReturnType = MyReturnType<fnType>
// type fooReturnType = string
type fooReturnType = MyReturnType<typeof foo>
InstanceType
由T的构造函数的实例组成的类型
type MyInstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : never