阅读 584

还在犹豫学不学TS? 肝就对了╮( ̄▽ ̄)╭

前言

大家都知道Vue3开始全面支持Typescript,而且ts本身支持es5和es6写法,所以学起来其实并不复杂,难度大概和刚开始js学完学es6难度差不多,趁着这两天有时间,自己总结下,欢迎评论留言交流,如有不对的地方,会及时更正,还望见谅,这些都是我个人笔记总结的

  • ts有点多 这肯定是个体力活 希望大家能给个三连 >B<

TS铺垫

TS的优势

TypeScript 的优势和收益是什么?

  • 类型系统可在编译阶段发现大部ipt 降低了使用成本

TS安装

  • 1.安装 TypeScript
$ npm install -g typescript
复制代码
  • 2.编译 TypeScript 文件
$ tsc helloworld.ts
复制代码

TS中文网

TS在线运行代码


TS的基本类型

number数字类型

let count: number = 666;

// ES5:var count = 666;
复制代码

String字符串类型

let csgo: string = "rush B";

// ES5:var csgo = 'rush B';
复制代码

Boolean 类型

let isOver: boolean = true;

// ES5:var isOver = true;

复制代码

Array数组类型

let arr: number[] = [1, 2, 3];
// ES5:var arr = [1,2,3];

let arr: Array<number> = [1, 2, 3]; // Array<number>泛型语法
// ES5:var arr = [1,2,3];
复制代码

Object对象类型

const obj:object={}
复制代码

以上在js类型中也有,那么下面介绍下ts比js多出的一些类型

Any 任何类型

在 TypeScript 中,任何类型都可以被归为 any 类型。 能被任何人赋值

let coco: any = 666;
coco = "字符串也可以哦";
coco = false;
复制代码

Enum 枚举类型

理解为存常量的特殊对象即可

enum Animal {
  CAT = 'cat',
  DOG = 'dog',
  MOUSE = 123,
}
console.log( Animal.CAT )//cat
console.log(Animal['CAT'])//cat
复制代码

枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射:

enum Days {Sun, Mon, Tue, Wed, Thu};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Thu"] === 4); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
复制代码

当然你也可以设置初始值

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
复制代码

Unknown 未知类型

就像所有类型都可以被归为 any,所有类型也都可以被归为 unknown any用来约束 ,而 unknown用来断定为不确定值

let value: unknown;

value = true;             // OK
value = 42;               // OK
//和any好像?
复制代码

未知类型只能赋值给自身以及any

let value: unknown;

let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
复制代码

Tuple 元祖类型

固定长度,固定类型

let tup: [number, string] = [27, "jianan"];
console.log(tup);//let tup: [number, string]
复制代码

Void 空类型

约束函数的返回值,表示该函数没有返回任何值 也就是 不return

// 声明函数返回值为void
function warnUser(): void {
  console.log("不会return任何东西");
}
复制代码
当然你不写void, ts类型推导会推出来你没有return 就会显示为void
当然你不写void, ts类型推导会推出来你没有return 就会显示为void

Never 永不存在值类型

表示该函数永远不会结束 比如报错类型语句 死循环

function error(message: string): never {
    throw new Error(message);
}
复制代码

接下来我们补充一些关键字,以便我们学习Type和接口以及泛型能够更好地理解


断言

也就是强制性的修改其数据类型

类型断言as

  • as断言

  • <断言>值

let someValue: any = "this is a string";

// 'as'语法:值 as 类型
let strLength: number = (someValue as string).length;

// '尖括号'语法:<类型>值
let strLength2: number = (<string>someValue).length;
复制代码

非空断言

确定其有值,不会null和undefiend

 function sayHello2(name: string | undefined) {
    let sname: string = name!;//!感叹号哦 就这个
  }
复制代码

  


类型判断

typeof 类型演算

const a: number = 3
// 相当于: const b: number = 4
const b: typeof a = 4
复制代码

instanceof 推断类型

let obj = 0.5 < Math.random() ? new String(1) : new Array(1);
if(obj instanceof String){
    // obj推断为String类型
    obj+= '123'
} else {
    // obj为any[]类型
    obj.push(123);
}
复制代码

类型别名 和 接口

这2个算是TS中比较重要用的也较多的,都可以用于约束 类、对象、函数的契约

先来写简单的

type one = {
    name: string,
    age: number
}
let obj: one = {
    name: '嘤嘤嘤',
    age: 18
}
复制代码

再看看接口

interface two {
    love: string,
    disg: () => boolean
}
let obj: two = {
    love: '勒布朗詹姆斯',
    disg: () => true
}
复制代码
  • Type和interface非常的相似 再看看他们应用在各种类型上如何约束↓

约束类

interface ClockInterface {      // 同理type也可以  type ClockInterface={}
    currentTime: Date;
    setTime(d: Date): string;
}

class Clock implements ClockInterface {
    public currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
        return '嘿嘿嘿'
    }
    constructor(h: number, m: number) { }
}
复制代码

约束对象

type和interface都可以
interface Husband {     //type Husband ={}
    sex:string
    interest:string
    age:number
}
let myhusband:Husband ={ sex:'男',interest:'看书、作家务',age:18}
复制代码

约束函数

//type和interface都可以
interface SearchFunc {   //type SearchFunc={}
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc = (source, subString) => { 
    return true;
}
复制代码

约束数组

//type和interface都可以
interface NumberArray {   //type NumberArray = {}
    [index: number]: number;
}

let fibonacci: NumberArray = [1, 1, 2, 3, 5];
复制代码

约束对象 属性值和属性名

//type和interface都可以    
interface Dictionary<T> {   //type Dictionary = {}
  [index: string]: T; //属性名:string : 属性值外界定
};

const data:Dictionary<number> = {
  a: 3,
  b: 4
}
复制代码

类型别名和接口的区别

继承方法不同

Type是使用交叉类型&进行继承,而interface使用的是extends 他们还能互相继承?

  • 接口 继承 接口(extends)
interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

const obj:User={
    name:'123',
    age:321
}
复制代码
  • 别名 继承 别名 (&)
type Name = { 
  name: string; 
}
type User = Name & { age: number  };

const obj:User={
    name:'123',
    age:321
}
复制代码
  • 接口 继承 别名

type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
const obj:User={
    name:'123',
    age:321
}
复制代码
  • 别名 继承 接口
interface Name { 
  name: string; 
}
type User =  { 
  age: number; 
}& Name
const obj:User={
    name:'123',
    age:321
}
复制代码

接口可以而别名不行的

interface有个特性,就是多次声明会进行合并

interface User {
  name: string
  age: number
}
interface User {
  sex: string
}
/*
User 接口为 {
  name: string
  age: number
  sex: string 
}
*/
复制代码

别名可以而接口不行的

type可以声明 基本类型,联合类型,元组 的别名,interface不行

// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]

//interface why = Pet|PetList //error
复制代码

type 支持类型映射,interface不支持 支持映射的type成为了泛型的宠儿

type Keys = "firstname" | "surname"

type DudeType = {
  [key in Keys]: string
}

const test: DudeType = {
  firstname: "Pawel",
  surname: "Grzybek"
}

// 报错
//interface DudeType {
//  [key in keys]: string
//}
复制代码

TS中的类

在 TypeScript 中,我们可以通过 Class 关键字来定义一个类:

class rushB {
    // 静态属性
    static cname: string = "rushB";
    // 成员属性
    heihei: string;

    // 构造函数 - 执行初始化操作
    constructor(message: string) {
        this.heihei = message;
    }

    // 静态方法
    static getClassName() {
        return "A1高闪来一个";
    }

    // 成员方法
    greet() {
        return "我就喜欢, " + this.heihei;
    }
}

let obj = new rushB("哈哈哈");

复制代码

访问权限修饰符

  • public: 默认的, 公开的,所有代码都能在内外访问到
  • private 私有的, 只有在类中才可以访问
  • protected 受保护的修饰符(只能在自身和派生类(子类)中访问到)
  • static : 它们不需要实例化,而是直接通过类来调用:
class Hello {
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
    public name: string; // 这个是对后文this.name类型的定义
    public age: number;
    private password: '这是私有的,只有我自己能访问'
    protected file:'这个只有我活着继承我的子类可以访问'
    print() {
        return this.name + this.age;
    }
}

let hello = new Hello('name', 18);
复制代码

存取器

设置器set / 读取器get 控制属性的读取和赋值

class Hello {
    private _age: number;
    get age(): number {
        return this._age;
    }
    set age(age: number) {
        if (age > 0 && age < 100) {
            console.log("年龄在0-100之间"); // 年龄在0-100之间
            return;
        }
        this._age = age;
    }
}

let hello = new Hello();
hello.age = 23;
console.log(hello.age)
复制代码

类继承+约束

interface A {
    age: number
}
class B {
    constructor(name: string) {
        this.name = name;
    }
    name: string
}
class C extends B implements A {
    constructor() {
        super('BBBBBB')
    }
    age = 123;
    tell() { console.log(this.age, this.name) }
}

let hh = new C()
hh.tell();//123  BBBBBB
复制代码

补充关键字

接下来准备泛型了 算是TS中的重量级选手 我们需要补充一些知识点

keyof 获取类型约束

interface Point {
    x: number;
    y: number;
}

type keys = keyof Point;// type keys = "x" | "y"
复制代码

in 配合keyof可以进行遍历

interface Duck {
    age: 10
    name: 'duck'
}

type obj = {
    [p in keyof Duck]: Duck[p] // age: 10   name: 'duck'
}
复制代码

下面2个用到了泛型 大家可以先去泛型看完再回头看,因为我不想又分开来免得大家来回跑

extends 继承

extends 可以用来继承一个类,也可以用来继承一个 interface,但还可以用来判断有条件类型

T extends U ? X : Y;
复制代码
  • 用来表示类型是不确定的, 如果U的类型可以表示T, 那么返回X, 否则Y.
  • 挺绕的那看看这个把
type Words = 'a'|'b'|"c";

type W<T> = T extends Words ? true : false;

type WA = W<'a'>; // -> true
type WD = W<'d'>; // -> false

复制代码

a 可以赋值给 Words 类型,所以 WA 为 true,而 d 不能赋值给 Words 类型,所以 WD 为 false。

infer 类型推导

就是字面意思 推断,柯南,懂? -.-

  • 看看源码实现
type ReturnType<T> = T extends (
  ...args: any[]
) => infer R
  ? R
  : any;
复制代码

其实这里的 infer R 就是声明一个变量来承载传入函数签名的返回值类型, 简单说就是用它取到函数返回值的类型方便之后使用。

我自己把自己写懵了,上面话我自己都听不太懂,话说infer真的难理解,看看下面的例子

T extends (...args: infer P) => any ? P : never;

  • 上面声明一个P用来表示...args可能的类型,
  • 如果(...args: infer P)可以表示 T, 那么返回...args对应的类型, 也就是函数的参数类型, 反之返回never.
  • 更好理解的是 infer P 他接收了P 并且无论如何都返回 因为他的判定条件为any 肯定返回 说实话我不知道是不是这样理解的,我只能大概的理解成这样,希望大佬看到能完善下小弟的意思

泛型

泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持

  • 来看看怎么用的吧 冲冲冲

2种定义泛型方式

function gen_func1<T>(arg: T): T {
    return arg;
}
复制代码
let gen_func2: <T>(arg: T) => T = function (arg) {
    return arg;
}
复制代码

使用泛型

function identity <T>(value: T) : T {
   return value;
 }
 
 console.log(identity<Number>(1)) // 1  Number传入到了 identity中的所有T中
复制代码


多泛型

这里的返回值还用到了联合类型哦

function CSGO <T, U>(value: T, message: U) : T|U {
  console.log(message);
  return value;
}

console.log(CSGO<Number, string>(66, "RushB"));
复制代码


泛型接口/别名

  • 类型别名和接口差不多的
interface CSGO<V, M> {
    target: V,
    message: M
}

let obj: CSGO<string, number> = {
    target: 'B',
    message: 666
}
复制代码


泛型类

interface GenericInterface<U> {
    value: U
    getIdentity: () => U
  }
  
  class IdentityClass<T> implements GenericInterface<T> {
    value: T
  
    constructor(value: T) {
      this.value = value
    }
  
    getIdentity(): T {
      return this.value
    }
  
  }
  
  const myNumberClass = new IdentityClass<Number>(24);
  console.log(myNumberClass.getIdentity()); // 24
  
  const myStringClass = new IdentityClass<string>("累了,复制粘贴例子算了!");
  console.log(myStringClass.getIdentity()); // 累了,复制粘贴例子算了!
  
复制代码

泛型参数默认类型

interface A<T=string> {
  name: T;
}

const strA: A = { name: "老八蜜汁小伙伴" };
const numB: A<number> = { name: 404 };
复制代码


泛型工具类型

这里就开始舒服起来了,封装了许多好用的

Partial 将类型T的成员变为可选

interface User {
  id: number;
};
// 相当于: type PickUser = { id?: number}
type PickUser = Partial<User>

//实现原理//
type Partial<T> = {//所有为可选-?
[P in keyof T]?: T[P];
};
复制代码

Required 将类型T中的成员变为必填

type User = {
  id: number;
};
// 相当于: type PickUser = { id: number}
type PickUser = Partial<User>

//实现原理//
type Partial1<T> = { //所有为必选
[P in keyof T]-?: T[P];
};
复制代码

Readonly 将类型T中的成员变为只读

//使用方式
type PickUser = Readonly<User>
//实现原理//
type Partial1<T> = { //所有为必选
  readonly[P in keyof T]: T[P];
};
复制代码

Exclude<T,U> 抽离出T可以给U赋值的值

``` js
复制代码

//使用方法 type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'> //type A = 'a'

//实现原理// type Exclude<T, U> = T extends U ? never : T;



## Extract<T,U> 抽离出T和U的共同值
``` js
type A = Extract<'x' | 'a' | 'c', 'x' | 'y' | 'a'>
//type A = 'x'|'a'

//实现原理//
type Extract<T, U> = T extends U ? T : never;
复制代码

ReadonlyArray 使数组变为不可赋值

let arr: ReadonlyArray<string> = ["a", "b"];
//arr:readonly string[]
arr.push("c"); // error
arr[0] = "c"; // error
复制代码

NonNullable 去除T中的null和undefined

``` js
复制代码

type User = 'a' | 'b' | null | undefined

type aaa = NonNullable // aaa = 'a'|'b'



## Parameters<T>获取函数参数的类型,返回数组形式
```js
function cba(a: number, b: string) {return a + b}
type nba = Parameterss<typeof cba>
//nba = [number,string]

//实现原理//
type Parameterss<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
复制代码

ConstructorParameters获取class构造函数的参数类型,返回数组形式

class abc{}
type aaa= new(a:number ,b:any)=> abc
type nba = ConstructorParameters<aaa>
//nba = [number,any]
复制代码

ReturnType 获取函数 返回值 类型

function abc(a: number, b: number) {
  return `${a + b}`//string
}
type cba = ReturnType<typeof abc>//typeof判断返回值为string
//cba=string

//实现原理//
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
复制代码

InstanceType获取构造函数类型的实例成员

//例子1class Animal {}
type Result = InstanceType<typeof Animal>;//类型为Animal
//例子2
class abc{}
type z = new()=>abc
type Result2 = InstanceType<z>;//类型为abc
复制代码

ThisType 设置对象的this

interface Person {
    name: string
    age: number
}
//[k: string]: any  对象的每一位属性名为字符串 属性值为anyZ
type ObjType = { [k: string]: any } & ThisType<Person>
const obj: ObjType = {
    a:1,
    method(arg1: boolean) {
        // this的类型被约束为Person
        console.log(this.age)
    }
}
复制代码

Omit<T,K> 将K属性从T校验对象中剔除掉

interface User {
 id: number;
 age: number;
 name: string;
};

// 将id从校验对象中剔除  相当于: type PickUser = { age: number; name: string; } 
type OmitUser = Omit<User, "id">
//实现原理//
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
复制代码

Pick<T,K>, 只保留自己选择的属性, K代表要保留的属性键值

这个有什么意义不懂

type A  = Pick<{a:number,b:string,c:boolean}, 'a'|'b'>
type A1 = Pick<A, 'a'|'b'> //  {a:number,b:string}
复制代码

中午1点到现在,接近4小时终于完成了,TS真的是多,希望可以帮到大家,这些是我学习ts记录的笔记分享给大家 (^▽^) 给个三连哦