ts简介
ts是以js为基础的语言
是js的超集,
拓展了js,
添加了类型
ts不能被js解析器直接执行,需要把ts编译为js
可以编译为任意版本的js
便于维护
ts的安装与运行
-
安装ts
npm install -g typescript
-
查看ts版本
tsc -v
-
将ts文件转为js文件
tsc 文件名.ts
-
执行js文件
node 文件名.js
简化上述执行过程
npm i -g ts-node
npm init -y
生成package.json
文件- 安装声明文件
npm i @types/node -D
- 直接执行ts文件
ts-node xxx.ts
数据类型
any、unknown
Object
Number、String、Boolean、
number、string、boolean、
1、'str'、true
never
number
let n: number = 1
console.log(n) //1
string
let s: string = 'hello,ts!'
console.log(s) //hello,ts!
boolean
let flag: boolean = true
if (flag) {
console.log('hello') //hello
} else {
console.log('ts')
}
null、undefined
-
null
表示对象值缺失,空对象引用
console.log(typeof null) //object console.log(typeof undefined) //undefined
-
undefined
用于初始化变量为一个未定义的值
any、unknown
-
any
任意类型 当该变量的类型不确定时,可以定义为any 声明为any类型的变量 可以被赋予任意类型的值 也可以赋值给任意类型的变量
-
unknown
未知类型 声明为unknown的变量 可以被赋予任意类型的值 只能赋值给unknown或any类型的变量 unknown类型的变量读不到自身任何属性
void、never
-
void
用于标识方法返回值的类型, 表示该方法没有返回值
function sayHi(): void { console.log('hi!') } sayHi()
-
never
-
代表从不会出现的值
type t = number & string //type t = never
-
在联合类型中会被排除掉
type T = string | number | never //type T = string | number
-
never 类型的变量只能被 never 类型所赋值
let x: never // never 类型可以赋值给 never类型 x = (() => { throw new Error('exception') })()
-
返回值为 never 的函数可以是抛出异常的情况
// 返回值为 never 的函数可以是抛出异常的情况 function error(message: string): never { throw new Error(message); }
-
返回值为 never 的函数可以是无法被执行到的终止点的情况
// 返回值为 never 的函数可以是无法被执行到的终止点的情况 function loop(): never { while (true) {} }
-
数组
-
定义普通类型的数组
const arr: number[] = [1, 2, 3] const arr2: Array<number> = [1, 2, 3]
interface A { [index: number]: string } const arr: A = ['1', '2', '3']
-
定义对象类型的数组
interface Obj { name: string } const arr3: Obj[] = [{ name: 'ym' }]
-
定义二维数组
const arr: number[][] = [[1, 2, 3]] const arr2: Array<Array<number>> = [[1, 2, 3]]
-
定义函数中的剩余参数数组和伪数组
function fn(...args: number[]) { const arr: IArguments = arguments console.log(args, arr) } fn(1, 2, 3)
元组 tuple
用来表示已知元素数量和类型的数组,各元素的类型不必相同,但对应位置的类型需要相同
即数量和类型有限的数组
let yuanzu: [number, string, boolean] = [1, '123', true]
console.log(yuanzu) //[ 1, '123', true ]
枚举 enum
-
默认情况下,从0开始为元素编号;当red=3时,表示从3开始为元素编号
enum Color { // red, red = 3, pink, blue } let r: Color = Color.red let p: Color = Color.pink let b: Color = Color.blue // 当red没赋值时,打印结果为 0 1 2 console.log(r, p, b) // 当red没赋值3时,打印结果为 3 4 5
-
red 的值是被计算出来的。注意注释部分,如果某个属性的值是计算出来的,那么它后面一位的成员必须要初始化值。
function getValue() { return 3 } enum Color { red = getValue(), pink = 6,// 此处必须要初始化值,不然编译不通过 blue } let r: Color = Color.red let p: Color = Color.pink let b: Color = Color.blue console.log(r, p, b) //3 6 7 // 可以根据枚举的值可到名字 console.log(Color[6]) //'pink'
-
反向映射
// 上述例子中,可以根据枚举的值得到名字 console.log(Color[6]) //'pink'
-
数字枚举
enum Color { red, green, blue } console.log(Color.red) // 0 console.log(Color.green) // 1 console.log(Color.blue) // 2
-
增长枚举
enum Color { red = 10, green, blue } console.log(Color.red) // 10 console.log(Color.green) // 11 console.log(Color.blue) // 12
-
字符串枚举
enum Color { red = 'red', green = 'green', blue = 'blue' }
-
异构枚举
enum Color { yes = 1, no = 'no' }
-
接口枚举
enum Color { red = 'red', green = 'green', blue = 'blue' } interface Obj { color: Color.red } const obj: Obj = { color: Color.red }
-
const枚举
enum Color { red, green, blue } console.log(Color.red) // 会编译为 var Color; (function (Color) { Color[Color["red"] = 0] = "red"; Color[Color["green"] = 1] = "green"; Color[Color["blue"] = 2] = "blue"; })(Color || (Color = {})); console.log(Color.red);
const enum Color { red, green, blue } console.log(Color.red) // 会编译为 console.log(0 /* Color.red */);
接口 interface
用接口定义对象类型的数据
-
接口重复定义会合并
-
任意属性(索引签名)
interface A { name: string [propName: string]: any }
-
可选属性 ?
interface A { age?: number }
-
只读属性 readonly
interface A { readonly id: string readonly fn: () => void }
-
接口继承 extends
interface A { } interface B extends A { }
-
定义函数类型
interface Fn { (name: string): number[] } const fn: Fn = function (name: string) { return [1] } const fn2: Fn = (name: string) => { return [1] }
函数类型
可选参数使用?标识
可选参数必须跟在必选参数后面
参数不能同时设为可选的和默认值
-
定义参数类型和返回值类型
function fn(a: number, b: number): number { return a + b } const fn2 = (a: number, b: number): number => { return a + b }
-
函数默认参数
function fn(a: number = 1, b: number = 2): number { return a + b } const fn2 = (a: number = 1, b: number = 2): number => { return a + b }
-
函数可选参数
function fn(a?: number, b?: number) {} const fn2 = (a?: number, b?: number) => {}
-
函数参数为对象时
interface User {} function fn(user: User): User { return user }
-
定义函数this类型
第一个参数指定this的类型
interface User { arr: number[] add: (this: User, num: number) => void } let user: User = { arr: [1, 2, 3], add(this: User, num) { this.arr.push(num) console.log(this.arr) } } user.add(4)
内置对象
let a: Number = new Number(123)
let b: String = new String('123')
let c: Boolean = new Boolean(true)
let d: Date = new Date()
let e: Error = new Error('出错了')
// HTML(元素名称)Element、HTMLElement、Element
let div: HTMLDivElement | null = document.querySelector('div')
let span: HTMLSpanElement | null = document.querySelector('span')
let ipt: HTMLInputElement | null = document.querySelector('input')
let divs: NodeListOf<HTMLDivElement> = document.querySelectorAll('div')
let localS: Storage = localStorage
let sessionS: Storage = sessionStorage
let lo: Location = location
let p: Promise<string> = new Promise(resolve => {
resolve('123')
})
类 class
-
在类中定义变量
不允许直接在constructor中使用变量,需要在constructor上面先声明
class Test { name: string constructor(name:string) { this.name = name } }
-
修饰符
public 公共的,都可以访问 private 私有的,只能在自身类中访问 protected 受保护的,只能在自身类及其子类中访问 static 静态属性和方法,只能通过类访问;静态的属性和方法中的this只能访问到静态属性和方法
-
class implements interface,interface
interface IP1 { name: string } interface Ip2 { age: number } class Person implements IP1, Ip2 { age: number name: string }
-
抽象类、抽象方法
abstract class A { abstract study(): void } // 无法创建抽象类的实例 // new A() class B extends A { // 非抽象类“B”不会实现继承自“A”类的抽象成员“study” study(): void { console.log('study') } }
-
综合案例
::: details
interface Options { el: string | HTMLElement } interface VueClass { options: Options init(): void } interface Vnode { tag: string text?: string children?: Vnode[] } class Dom { createElement(el: string) { return document.createElement(el) } setText(el: HTMLElement, text?: string | null) { el.textContent = text || null } render(data: Vnode) { let root = this.createElement(data.tag) if (data.text) { this.setText(root, data.text) } if (data.children) { data.children.forEach(item => { let child = this.render(item) root.appendChild(child) }) } return root } } class Vue extends Dom implements VueClass { options: Options constructor(options) { super() //调用父类的构造函数 父类的prototype.constructor.call this.options = options this.init() } init(): void { let data: Vnode = { tag: 'div', text: '我是div', children: [ { tag: 'span', text: '我是div中的span' } ] } let app = typeof this.options.el === 'string' ? document.querySelector(this.options.el) : this.options.el app?.appendChild(this.render(data)) } } const v = new Vue({ el: '#app' })
:::
extends
extends
关键字用于检查一个类型是否是另一个类型的子类型
-
extends关键字在【条件类型】中的使用
【分布式条件类型】:分布式条件类型是条件类型的一个特性,当条件类型遇到联合类型时,会被应用到联合类型的每一个成员上,这就是所谓的“分布式”行为。
例如,有一个条件类型
T extends U ? X : Y
,如果T
是一个联合类型A | B | C
,那么这个条件类型就会被展开为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
例子:
type A = number | string | boolean type B = boolean type CustomExclude<T, K> = T extends K ? never : T type C = CustomExclude<A, B> //type C = string | number
type C = CustomExclude<A, B> // 解释: // number extends boolean ? never : number -> number // string extends boolean ? never : string -> string // boolean extends boolean ? never : boolean -> never // 因此,Example 类型结果为 string | number
-
extends关键字在【泛型约束】中的使用
限制泛型参数的类型范围
//T 必须是一个具有 length 属性的类型。 function logLength<T extends { length: number }>(item: T): void { console.log(item.length); } logLength("hello"); // 合法,因为字符串有 length 属性 logLength([1, 2, 3]); // 合法,因为数组有 length 属性 logLength({ length: 10 }); // 合法,因为对象有 length 属性 logLength(123); // 错误,因为数字没有 length 属性
keyof
是类型操作符,用于获取某个类型的所有键名并构成联合类型
-
基本用法
interface Person { name: string; age: number; } // 使用 keyof 获取 Person 类型的所有键名 type PersonKeys = keyof Person; // "name" | "age"
-
在【泛型约束】中的使用
interface Person { name: string age: number } const p: Person = { name: 'ym', age: 120 } // K extends keyof T 表示泛型参数 K 必须是 T 类型的键名之一。 function getValueOfKey<T, K extends keyof T>(obj: T, key: K): void { console.log(obj[key]) } getValueOfKey(p, 'name') getValueOfKey(p, 'sex') //Argument of type '"sex"' is not assignable to parameter of type 'keyof Person'.
-
【keyof】结合【in】的使用
例:对类型
T
的每一个属性,都创建一个同名的可选属性,其类型与T
中对应属性的类型相同。interface Person { name: string age: number } type CustomPartial<T> = { [key in keyof T]?: T[key] } type Person2 = CustomPartial<Person> /** * type Person2 = { name?: string | undefined; age?: number | undefined; }
[key in keyof T]?: T[key]
-
keyof T
:keyof操作符获取到类型T的所有键名,并生成一个联合类型。此处生成'name'|'age'
-
[key in keyof T]
:这是一个映射类型语法,通过【in】遍历联合类型,并将每个键名映射到一个新的属性此key是临时变量,代表联合类型中的每个成员
-
T[key]
:这是【索引类型查询】,表示取出类型T中key属性的类型如 key为name时,T[‘name’]就是string
-
范型
基本使用
动态类型,把类型当做一个变量,通过 <>
传入
-
范型函数
function fn<T>(a: T, b: T): Array<T> { return [a, b] } fn<number>(1, 2)
function fn<T, U>(a: T, b: U): Array<T | U> { return [a, b] } fn(1, '2')
interface Data { code: number msg: string } const obj = { fn<T>(): Promise<T> { return new Promise((resolve, reject) => { resolve(JSON.parse('{"code":"0","msg":"success"}')) }) } } obj.fn<Data>().then(res => { console.log(res) })
-
范型接口
interface O<T> { msg: T } let obj: O<string> = { msg: 'hahha' }
-
范型type
type t<T> = number | string | T let a: t<boolean> = true
范型约束
extends
范型工具
Readonly
所有属性 只读
interface Person {
name: string
age: number
sex: string
}
type Person2 = Readonly<Person>
实现:
interface Person {
name: string
age: number
sex: string
}
type CustomReadonly<T> = {
readonly [key in keyof T]: T[key]
}
type Person2 = CustomReadonly<Person>
Partial
所有属性 可选
interface Person {
name: string
age: number
sex: string
}
type Person2 = Partial<Person>
实现:
interface Person {
name: string
age: number
sex: string
}
type CustomPartial<T> = {
[p in keyof T]?: T[p]
}
type Person2 = CustomPartial<Person>
Required
所有属性 必选
interface Person {
name?: string
age?: number
sex?: string
}
type Person2 = Required<Person>
实现:
interface Person {
name?: string
age?: number
sex?: string
}
type CustomRequired<T> = {
[p in keyof T]-?: T[p]
}
type Person2 = CustomRequired<Person>
Pick
提取部分属性
Pick
是 TypeScript 中的一个内置的工具类型,它可以实现从一个已有的类型中挑选出一部分属性,生成一个新的子类型。
interface Person {
name: string
age: number
sex: string
}
type Person2 = Pick<Person, 'name' | 'age'>
实现:
interface Person {
name: string
age: number
sex: string
}
type CustomPick<T, K extends keyof T> = {
[p in K]: T[p]
}
type Person2 = CustomPick<Person, 'name' | 'age'>
Exclude
排除type中的类型
Exclude<T, U>
是 TypeScript 中的一个内置工具类型,用于从一个联合类型 T
中把符合 U
的成员删除,生成一个新的类型。
type T = string | number | boolean
type T2 = Exclude<T, boolean> //type T2 = string | number
实现:
type T = string | number | boolean
type CustomExclude<T, K> = T extends K ? never : T
type T2 = CustomExclude<T, boolean>
Extract
取共同的类型
type A = number | string | boolean
type B = string | boolean
type C = Extract<A, B>//type C = string | boolean
实现:
type A = number | string | boolean
type B = string | boolean
type CustomExclude<T, K> = T extends K ? T : never
type C = CustomExclude<A, B> //type C = string | number
Omit
排除interface中的属性
Omit
也是 TypeScript 内置的工具类型,和 Pick
相反,Omit
用于从一个已有的类型中删除某些属性,来生成一个新的属性
interface Person {
name: string
age: number
sex: string
}
type Person2 = Omit<Person, 'name' | 'age'>
/**
* type Person2 = {
sex: string;
}
*/
-
实现方式一:借助【Pick 和 Exclude】
interface Person { name: string age: number sex: string } type CustomOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> type Person2 = CustomOmit<Person, 'name' | 'age'>
-
实现方式二:借助【Exclude】
interface Person { name: string age: number sex: string } type CustomOmit<T, K extends keyof T> = { [key in Exclude<keyof T, K>]: T[key] } type Person2 = CustomOmit<Person, 'name' | 'age'>
-
实现方式三:不借助【Pick 或 Exclude】
interface Person { name: string age: number sex: string } type CustomOmit<T, K extends keyof T> = { [key in keyof T as key extends K ? never : key]: T[key] } type Person2 = CustomOmit<Person, 'name' | 'age'>
NonNullable
从type 中排除掉 null 和 undefined
type A = string | number | null | undefined
type B = NonNullable<A>
实现:
type A = string | number | null | undefined
type CustomNonNullable<T> = T extends null | undefined ? never : T
type B = CustomNonNullable<A>
Record
约束对象的key和value
type Key = 'name' | 'age'
type Value = number | string
let obj: Record<Key, Value> = {
name: 'ym',
age: 120
}
实现:
type Key = 'name' | 'age'
type Value = number | string
type ObjKey = keyof any // type ObjKey = string | number | symbol
type CustomRecord<K extends ObjKey, V> = {
[p in K]: V
}
let obj2: CustomRecord<Key, Value> = {
name: 'ym',
age: 120
}
ReturnType
获取函数返回值类型
const fn = () => '123'
type res = ReturnType<typeof fn>
实现:
type CustomReturnType<F extends Function> = F extends (...args: any[]) => infer Res ? Res : never
type res2 = CustomReturnType<typeof fn>
infer
infer用来推导范型参数
infer声明只能出现在extends子语句中
interface User {
name: string
age: number
}
type PromiseType = Promise<User>
type GetPromiseType<T> = T extends Promise<infer U> ? U : T
type T = GetPromiseType<PromiseType> //type T = User
联合类型、交叉类型
-
联合类型|
通过管道符|将变量设置多种类型
let arr: (number | string | boolean)[] = [1, '2', true]
-
交叉类型 &
interface A { name: string } interface B { age: number } let user: A & B = { name: 'ym', age: 120 }
类型断言
两种写法
变量 as 类型
<类型>变量
类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误
function fn(num: number | string): void {
console.log((num as string).length)
console.log((<string>num).length)
}
fn('123')
类型别名 type
type S = string
let str: S = '123'
type和interface的区别
-
interface使用extends实现继承;type使用交叉类型
interface A { name: string } interface B extends A { age: number }
type C = { name: string } type D = { age: number } & C
-
type中可以写联合类型;interface只能在内部属性上写联合类型
interface A { name: string | number } type B = string | number
-
重名的interface 类型会合并;重名的type会报错
类型推断
-
当没有给出变量的类型时,ts编译器会利用类型推断来推断类型
var num=2 //var num: number num='123'//编译错误 不能将类型“string”分配给类型“number”。
-
如果由于缺乏声明而不能推断出类型,那么此变量的类型为any类型
var a //var a: any a=1 a='123'
tsconfig.json
tsc --init
命令会生成tsconfig.json文件
tsc -W
监视该文件夹下的所有ts文件的变化,自动编译该文件夹下的所有ts文件
// tsconfig.json文件是ts编译器的配置文件,ts编译器可以根据配置进行编译
{
// 编译器选项
"compilerOptions":{
"target":"es6",//指定ts被编译为es的版本
"module":"es6",// 指定要使用的模块化规范
// "lib": ["es6","DOM"],// 指定项目中要使用的库,一般不配置
"outDir": "./dist",// 用于指定ts编译为js后js文件的存放目录
// "outFile": "./dist/app.js",// 将编译后的js代码放到一个文件中,此时"module":"system",或"module":"amd"
"allowJs": false,// 编译js文件?
"checkJs": false,// 检查js代码是否符合规范?
"removeComments": true,// 移除注释?
"noEmit": false,// 不生成编译后的文件?
"noEmitOnError": false,// 当有错误时不生成编译后的文件?
"alwaysStrict": true,// 使用严格模式?,启用后,编译后的js文件会"use strict";
"noImplicitAny": true,// 不允许隐式的any?
"noImplicitThis": true,// 不允许不明确类型的this?
"strictNullChecks": true,// 严格检查空值?
"strict": true,// 所有严格检查的总开关
},
// "include":[]用来指定哪些ts文件需要被编译
// ** 表示任意目录
// * 表示任意文件
"include": [
"./src/**/*"
],
// "exclude":[]用来指定不需要被编译的ts文件
// 默认值为"node_modules","bower_components", "jspm_packages"
"exclude": [
"node_modules",
"bower_components",
"jspm_packages"
],
// 继承,相当于引入某个文件
// "extends": "",
// files指定要编译的具体ts文件
// "files": []
}
声明文件 xxx.d.ts
declare
当使用第三方库时,需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能