ts类型的分类: 基础类型、高级类型、内置类型、自定义类型、类型体操
- ts是为了考虑在赋值的时候是否会发生错误,作类型检测,起到一个提示作用,编译后ts类型就消失了,生产环境下可以通过xxx.d.ts文件来增加类型生命
- 并非所有变量都要赋类型,因为ts可以类型推导
基本类型
string number boolean 数组 元组 枚举 null undefiend void never any object symbol bigInt
- void代表的是空类型,一般值表示函数的返回值,表示不关心返回值
- unefiend可以赋予给void,都代表空 (
undefiend 是 void的子类型) - never只能被never类型来赋予值,用来做代码完整性保护
- any 任何类型,会导致类型丧失检测
const a1 = 1
const u: undefined = undefined
const n: null = null
function a(): void {
return undefined
}
const symbol:symbol = Symbol()
const bigint:bigint = BigInt(Number.MAX_SAFE_INTEGER + 1)
// const 是常量意味着定义的值不会修改所以他的类型是一个字面量类型 . const声明变量必须复制
// let 声明变量 可以修改所以类型范围推到的结果会变大
const a1 = 1
let a2 = 1
包装类型
- 小写的类型一般用于描述基本类型 大写的用来描述的是实例类型
let s3: String = '1'; // 在赋予值的时候 子集可以赋予给父级
let s4: String = new String('1'); // 类的类型,类类型,用来描述实例的
数组/元组 tuple
- 赋予的值要求得符合这个结构和顺序
- 元组在新增内容的时候 不能增加额外的类型的值,只能是已有的,而且增加后无法访问
// 三种写法
let arr1: number[] = [1, 2, 3, 4, 5];
let arr2: Array<number> = [1, 2, 3, 4, 5];
let arr3: (number | string)[] = [1, 2, 3, 'a', 'b', 'c'];
let tuple: [string, number, string, number] = ['1', 1, '1', 1]
枚举 enum
- .类型可以进行反举 (值是数字的时候 可以反过来枚举), 枚举没有值会根据上面的索引来自动累加
- 异构枚举 就是枚举中不光有数字 还有字符串. 异构枚举上一个是字符串下一个无法推导
const enum STATUS { // 常量枚举 不会额外编译成对象, 所以更节约
'OK' = 'ok',
'NO_OK' = 100,
'NOT_FOUND'
}
联合类型
!是非空断言,表示某个变量一定存在as是指定成某种类型ele?.style.background 表示的是取值操作,不能赋值
let strOrNum :string | number;
// !TS语法 ?js语法 链判断运算符(如果值有再去取值)
let ele = document.getElementById('app');
// 断言方式1.
ele!.style.background = 'red';
// 断言方式2.
(ele as HTMLElement).style.background = 'red';
// 类型别名将类型提取出来
type Direction = 'up'|'down'|'left'|'right'; // 快速构建一个可以复用的类型
let direction:Direction
let up:'down' = direction! as 'down'
// 值 as xxx 或者 <xxx>值 一般用于联合类型. 将大范围的类型 断言成子类型
(strOrNum! as unknown as boolean); // 双重断言,一般不建议使用,但是还会用到,破坏原有的关系
函数类型
函数的声明方式、函数的参数、函数的返回值
- 函数关键字声明的函数:不能标注函数类型
- 表达式来声明的函数: 必须赋予的值要满足定义的类型,要求有一个兼容性在里面
- 函数类型的定义
(a: any, b?: string) => any|(a: any, b: any) : any - ? 可选参数 意味着可以不传 和
string | undefiend,只能在参数列表中的后面 - this问题,尽量不采用this 来作为函数的上下文, this的缺陷就是类型推导问题
- 函数重载
type ISum = {(a: any, b: any) : any}
type ISum2 = (a: any, b: any) => any
// 如果标明函数的类型,在使用函数的时候以标明的为准
const sum:ISum = function(a:string,b:string){
return a+b
}
// 如果想限制this类型 那么需要手动指定this类型
function getValue(this: IPerson, key: IKeys) { // this不是行参 是标明this的类型
return this[key];
}
// keyof 可以获取对象中的类型 作为联合类型
const person = { name: 'jw', age: 30, address: '昌平霍营' }
type IPerson = typeof person; // 提取对象的类型为IPerson, type类型会提升到顶部
type IKeys = keyof IPerson;
// 可以将子类型赋予给父类型
getValue.call(person, 'age')
// string[] | number[] (number|string)[]
function toArray(value: string): string[]// 具体的某一种方案
function toArray(value: number): number[];
// 上面的声明仅仅是类型上的重载
function toArray(value: string | number): string[] | number[] { // 所有的实现
return []
}
typeof、keyof关键字
- typeof:根据值来获得类型 typeof, 配合type来声明新的类型
- keyof: 可以获取对象中的类型 作为联合类型
type IPerson = { name: string, age: number, address: undefined }
// 等价于下面代码
const person = { name: 'jw', age: 30k, address: undefined }
type IPerson2 = typeof person;
// 等价于下面代码
type IKeys = "name" | "age" | "address"
type IKeys2 = keyof IPerson;
type IKeys1 = keyof any; // string | number | symbol 使用场景比较多
type IKeys2 = keyof unknown; // never
class类型
- 构造函数
- 属性(实例属性,原型属性、静态属性)、
- 方法 (实例的方法,原型方法,静态方法)
- 访问器
- 类的修饰符
- 抽象类
class Animal {
/* 静态属性 可以被子类继承的 */
static AnimalName: string = "动物"
static instace: Animal = new Animal("aaa",11)
// 静态方法
static getMessage(): Animal {
return this.instace
}
// static getMessage: (text: string) => string = () => {
// return text
// }
public readonly y: number
a = "1" // 实例的属性a
public fn: () => void //实例有一个属性叫fn
public foo (num: number): void { } // 类的原型方法foo
/*
类的修饰符 可以简写一行
public 公共
private 自己 如果加在constructor前面,不能被new,只能在类自身里面new实例
protected 我和儿子
readonly是只读的,在constructor初始化后就不能被修改了
*/
protected constructor(public name: string, y : number) {
this.name = name;
this.y = y;
this.fn = () => {}
}
/*
原型属性:通过访问器来实现 Animal.prototpye.aliasName
*/
get aliasName () {
return 'msk-' + this.name
}
/*
原型方法,每个实例共享的方法:父类的方法,可以被子类重写 Animal.prototpye.changName
void表示不关心返回值。并不是空的意思
*/
changName(name: string): void {
this.name = name
}
}
class Cat extends Animal {
constructor(name: string, age: number) {
// super这里指向的父类
super(name, age)
}
changName(name: string) {
console.log(this,);
// super在这里(原型方法中)指向的父类的原型
super.changName("aaa")
this.name = name
}
}
const c = new Cat("haha",18)
c.changName("eee")
console.log(c.aliasName);
console.log(Cat.AnimalName);
console.log(Cat.getMessage());
抽象类
// ts 中有抽象类概念, abstract 不存在的
// 抽象类 可以含义非抽象的方法和属性 , 不会new它 , 抽象类可以被继承,抽象类中抽象方法子类必须要实现
abstract class Animal{
public abstract a:string
drink(){ // 非抽象,已经有实现了
console.log('喝水')
}
abstract eat():void // 抽象的方法,父类没有实现,那么子类必须实现
}
class Cat extends Animal{
public a!: string;
eat(): void {
throw new Error("Method not implemented.");
}
}
// 因为我们编写的代码的时候 , 慢慢的脱离继承了。 组合优于继承。 类的装饰器(redux,nest, mobx)
接口
- 描述数据的结构、或者形状的, 定义好结构,在去针对结构来进行实现
- 接口: 抽象类(有抽象的也有非抽象) 接口必须都是抽象的(没有具体的实现)
- type 和 interface 区别?
- 一般情况下 描述对象,类 用interface更多一些 ,interface无法声明联合类型
- type 可以快速声明类型 联合类型,工具类型只能采用type , type不能重名
- type用的更多,能用type 就type 不能用interface. 复杂类型采用type
接口可以描述对象结构 (子可以赋予给父)
interface IPerson {
username: string // 类型,不是具体实现
age:number
}
let obj = {
username:'abc',
age:30,
address:'地址'
}
// 赋值的时候 会产生兼容性 (儿子可以赋予给父亲,如果是声明的必须必须一致)
let person:IPerson = obj;
// 2) 接口可以描述函数
interface ICounter {
():number // ()表示函数参数类型 number是函数的返回值类型
count:number
}
// const 标识此值不能修改 let 可以修改的 (如果给函数增加类型定义 函数不能被修改时只能用const)
const counter:ICounter = () =>{
return counter.count++
}
counter.count = 0;
接口特性
- 可以通过?表示接口的属性 可有可无
- 断言的方式来进行赋值, 用的最多 as IVeg
- 接口同名的会进行合并
- 可以扩展一个新类型 可以扩展属性
- 任意类型 随机的属性 描述数字索引的 (除了必有的属性之外 ,其他任意)
interface Person {
name: string
age: number
}
// 2.断言:保证p以后肯定不用age,出了问题就怪写as的人
const p: Person = { name: 'msk' } as Person
interface IVeg { // 3.合并
address: string
}
interface IVeg { // 3.合并
readonly name: string // 只读属性
taste: string;
size: number,
}
interface IVegetable extends IVeg {
color?: '红色', // 1.可选
[key: string]: any // 4.扩展 任意接口 key 随意,值随意
}
const veg: IVegetable = {
name: '西红柿',
taste: '甜',
size: 50,
color: '红色',
'a': 1,
'b': '2',
[Symbol()]: 'ABC',
0: 1
}
interface IArray { // 5. 索引接口
[key: number]: any
}
let arr:IArray = [1,2,3]
let arr2: IArray = { 0: 1, 1: 2, 2: 'abc', 3: true }
interface ResponseData {
username: string,
token: string
}
interface ReturnVal {
code: number,
data: ResponseData
}
// 通过索引访问符 来获取内部类型
type ICode = ReturnVal['code'];
type IUsername = ReturnVal['data']['username']; // 可以用于取值的类型
type IKeys = ReturnVal[keyof ReturnVal]; // 取值的类型, 可以采用这种方式
// Ikeys = number | ResponseData
implements 定义类的实现
- 一个类 可以实现多个接口
interface SpeakChinese {
speakChinese(): void
}
interface SpeakEnglish {
speakEnglish(): void
}
class Speak {
public a!: string;
}
interface Speakable extends SpeakEnglish, SpeakChinese, Speak {
speak(): void // 实现的是原型方法
// speak:()=>void // 实现的是实例方法
}
class Speaker implements Speakable {
public a!: string;
speakEnglish(): void {
throw new Error("Method not implemented.");
}
speakChinese(): void {
throw new Error("Method not implemented.");
}
speak() {
return 100
}
}
泛型
class Dog {
constructor(public name: string) { } // new
}
class Cat {
constructor(public name: string) { }
}
// 描述构造函数
// interface IClazz<T>{
// new (name:string): T
// }
type IClazz<T> = new (name: string) => T
// createInstance<T>是形参
function createInstance<T>(clazz: IClazz<T>, name: string) {
return new clazz(name)
}
const instance = createInstance(Cat, 'tom')
// <dog>是实参,不传也行
const instance2 = createInstance<Dog>(Dog, 'jam')
// 泛型: 泛型坑位 (函数的形式参数) 刚开始类型不确定,通过使用的时候 来确定类型
泛型如何使用
const fn = <T>(name: T): T => name
// function 函数名:<泛型 > (参数名: <泛型>): <T> 返回值
function foo<T> (name: T): T[] {
return [name]
}
// <T>():void 在使用这个函数的函数传入类型
interface ForEach {
<T>(arr: T[], callback: (val: T) => void): void
}
type ICallback<T> = {(val: T):void} //
type ICallback2<T> = (val: T) => void
type IForEach = <T>(arr: T[], callback: ICallback<T>) => void
案例
function swap<T,K>(tuple:[T,K]):[K,T]{
return [tuple[1],tuple[0]]
}
const r = swap(['jw',true])
泛型默认值
// 配合接口来使用
interface APIResponse<T = any> {
error: number,
data: T,
message?: string
}
interface LoginInfo {
username: string,
token: string
}
function login(): APIResponse<LoginInfo> {
// function login(): APIResponse { // 请求方法
return {
error: 1,
data: {username: '张三', token: 'xxxx'},
message: '成功'
}
}
let w = login()
泛型约束
type Unique<T = boolean> = T | string
// 类型约束 子类型 extends 父类型 子类型可以赋给父类型,只要满足父类型的结构
// 约束当前这个类型 K string或number的子类型 (因为在使用泛型的时候参数不能直接做运算,无法确定类型,需要约束)
const getLen = <T extends { length: number | string}, K extends string | number>(val: T, isFlag: Unique, join: K) => {
return val.length
}
getLen({length: 10}, '11', 111)
getLen('123', false, '2222')
function getObjVal<T extends object, K extends keyof T>(target: T, key: K) {
return target[key]
}
let person = { name: 'jw', age: 30 };
let animal = { name: 'tom', age: 18, address: 'china' };
getObjVal(animal, "address");
类中的泛型
class MyList<T extends string | number = number> {
private arr: T[] = []
add(val: T) {
this.arr.push(val)
}
getMax(): T {
let max = this.arr[0];
for (let i = 1; i < this.arr.length; i++) {
let cur = this.arr[i];
cur > max ? (max = cur) : void 0
}
return max
}
}
const list = new MyList<string>
list.add('1')
list.add('200');
list.getMax(); // 200