kd-cloud-web / Blog

一群人, 关于前端, 做一些有趣的事儿
13 stars 1 forks source link

关于typescript类型碎片指北 #40

Open Hibop opened 4 years ago

Hibop commented 4 years ago

一、Why Ts

image

二、Ts历史、编译原理、生态环境 【略】

三、 类型keywords

基础类型

  • 布尔值 boolean
  • 数字 number
  • 字符串 string
  • 数组 let list: number[] = [1, 2, 3]; let (number | string)[] = [1,2, '3']
  • 元组(tuple) let x: [string, number]
  • 枚举 enum Color {Red, Green, Blue}; let c: Color = Color.Green;
  • any
  • void
  • null undefined 内置对象类型 Boolean、Error、Date、RegExp Document、HTMLElement、Event、NodeList

高级用法

  • 类型断言: <类型>值 OR 值 as 类型
  • 类型别名 type TypeAlise = string
  • 泛型: 相当于类型变量
  • 接口: interface
  • 注解: declare

四、talk is cheap, show me the code


// ➖➖➖➖➖➖➖➖➖布尔➖➖➖➖➖➖➖➖➖
// 布尔值
let isDone: boolean = false;

// 事实上 `new Boolean()` 返回的是一个 `Boolean` 对象
let createdByNewBoolean: Boolean = new Boolean(1);

//(直接调用 `Boolean` 也可以返回一个 `boolean` 类型)
let createdByBoolean: boolean = Boolean(1);
// 易错, Mark 大写和小写boolean区别

// ➖➖➖➖➖➖➖➖➖数值➖➖➖➖➖➖➖➖➖
// 数值
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;

// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;

// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;

// ➖➖➖➖➖➖➖➖➖字符串➖➖➖➖➖➖➖➖➖
let myName: string = 'Tom';

// ➖➖➖➖➖➖➖➖➖空值➖➖➖➖➖➖➖➖➖
// 没有返回值的函数为void
function alertName(): void {
    alert('My name is Tom');
}
// 声明一个 void 类型的只能将它赋值为 undefined 和 null
let unusable: void = undefined;

// ➖➖➖➖➖➖➖➖➖Null 和 Undefined➖➖➖➖➖➖➖➖➖
// undefined 类型的变量只能被赋值为 undefined,null 类型的变量只能被赋值为 null
let u: undefined = undefined;
let n: null = null;

// ➖➖➖➖➖➖➖➖➖ any ➖➖➖➖➖➖➖➖➖
// 顾名思义,可以被任何值赋值
let anyThing1: any = 'hello';
let anyThing2: any = 888;
let anyThing3: any = true;
let anyThing4: any = null;
let anyThing5: any = undefined;

// 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:
let any;
any = true;

// ➖➖➖➖➖➖➖➖➖ 类型推论 Type Inference ➖➖➖➖➖➖➖➖➖
// 如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
let myFavoriteNumber = 'seven'; // 等价于  let myFavoriteNumber: string = 'seven';

// 错误写法
let typeInferenceString = 'seven';
typeInferenceString = 'nine'; // OK
// typeInferenceString = 7; // Error

// ➖➖➖➖➖➖➖➖➖ 联合类型 Union Types ➖➖➖➖➖➖➖➖➖
// 联合类型(Union Types)表示取值可以为多种类型中的一种。
// 当你允许某个变量被赋值多种类型的时候,使用联合类型,管道符进行连接
let unionTypeStringOrType: string | number;
unionTypeStringOrType = 'seven';
unionTypeStringOrType = 7;

// 也可用于方法的参数定义, 都有toString方法,访问 string 和 number 的共有属性是没问题的
function getString(something: string | number): string {
    return something.toString();
}

////错误写法
// function getLength(something: string | number): number {
//     return something.length; // number 不存在length属相
// }

// ➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
// |              **接口类型 interface (对象的类型)
// ➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
// 赋值的时候,变量的形状必须和接口的形状保持一致(不能多也不能少,类型还必须一致)
// 接口的作用就是为这些类型命名
// 和为你的代码或第三方代码定义契约。

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};

interface IUserInfo{
    age: any;//定义一个任何变量的 age.
    userName: string;//定义一个 username.
}
function getUserInfo(user: IUserInfo): string {
    return user.age + "======" + user.userName;
}

// ➖➖➖➖➖➖➖➖➖可选属性// ➖➖➖➖➖➖➖➖➖
interface Person1 {
    name: string;
    age?: number; // 表示这个属性可有可无
}

let tom1: Person1 = {
    name: 'Tom'
};

// ➖➖➖➖➖➖➖➖➖任意属性// ➖➖➖➖➖➖➖➖➖

//希望一个接口允许有任意的属性,可以使用如下方式:旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
interface Person2 {
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom2: Person2 = {
    name: 'Tom',
    gender: 'male' // 可以加其他的属性
};

// ➖➖➖➖➖➖➖➖➖只读属性// ➖➖➖➖➖➖➖➖➖
interface Person3 {
    readonly id: number; //
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom3: Person3 = {
    id: 89757, // 只读
    name: 'Tom',
    gender: 'male'
};

// 错误写法
// 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
// interface PersonErr {
//     name: string;
//     age?: number;
//     [propName: string]: string;
// }

// let tomerr: PersonErr = {
//     name: 'Tom',
//     age: 25,
//     gender: 'male' // ❌
// };
// 上例中,任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。

// ➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
// |              **数组类型 Array
// ➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
let fibonacci1: number[] = [1, 1, 2, 3, 5];
let fibonacci2: Array<number> = [1, 1, 2, 3, 5];

// ➖➖➖➖➖➖➖➖➖用接口表示数组➖➖➖➖➖➖➖➖➖
interface NumberArray {
    [index: number]: number;
}
let fibonacci3: NumberArray = [1, 1, 2, 3, 5];

// ➖➖➖➖➖➖➖➖➖any 在数组中的应用➖➖➖➖➖➖➖➖➖
let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];

// ➖➖➖➖➖➖➖➖➖类数组➖➖➖➖➖➖➖➖➖
function arguments() {
    let args: IArguments = arguments;
}

// ➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
// |              **函数类型: 入参和返回类型必须
// ➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖

// 需要把输入和输出都考虑到
function sum(x: number, y: number): number {
    return x + y;
}

// ➖➖➖➖➖➖➖➖➖函数表达式➖➖➖➖➖➖➖➖➖
let mySum = function (x: number, y: number): number {
    return x + y;
};
// 不要混淆了 TypeScript 中的 => 和 ES6 中的 =>
let mySum2: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};
// ➖➖➖➖➖➖➖➖➖接口定义函数的形状➖➖➖➖➖➖➖➖➖
interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function (source, subString) {
    return source.search(subString) !== -1;
}

// ➖➖➖➖➖➖➖➖➖可选参数➖➖➖➖➖➖➖➖➖
function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let Tim = buildName('Tim');

// ➖➖➖➖➖➖➖➖➖参数默认值➖➖➖➖➖➖➖➖➖
function buildNameDefault(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}

// ➖➖➖➖➖➖➖➖➖剩余参数➖➖➖➖➖➖➖➖➖
// rest 参数只能是最后一个参数,关于 rest 参数,是一个数组
function push(array: any[], ...items: any[]) {
    items.forEach(function (item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

//// 错误栗子
// 在Ts中 输入多余的(或者少于要求的)参数,是不被允许的:
// function sumErr(x: number, y: number): number {
//     return x + y;
// }
// sumErr(1, 2, 3); // ❌
// sumErr(1); // ❌

//// 可选参数后面不允许再出现必须参数了:
// function genateName(firstName?: string, lastName: string) {
//     if (firstName) {
//         return firstName + ' ' + lastName;
//     } else {
//         return lastName;
//     }
// }
// let tomCat = genateName('Tom', 'Cat');
// let jim = genateName(undefined, 'Jim');

// ➖➖➖➖➖➖➖➖类型断言➖➖➖➖➖➖➖➖➖
// 可以使用类型断言,将 something 断言成 string
function getLength2(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}
// 只能访问此联合类型的所有类型里共有的属性或方法
// function getLength3(something: string | number): number { // ❌
//     return something.length;
// }

// ➖➖➖➖➖➖➖➖类型别名 alise➖➖➖➖➖➖➖➖➖
// 使用 type 创建类型别名,类型别名常用于联合类型
type Name = string;
type NameResolver = () => string; // good use
type NameOrResolver = Name | NameResolver; // good use
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

// ➖➖➖➖➖➖➖➖枚举 enum ➖➖➖➖➖➖➖➖➖
// 枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天
// 枚举就是枚举值到枚举名进行反向映射
// enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat } ==> Days
// {
//     "0":"Sun","1":"Mon","2":"Tue","3":"Wed","4":"Thu","5":"Fri","6":"Sat",
//     "Sun":0,"Mon":1,"Tue":2,"Wed":3,"Thu":4,"Fri":5,"Sat":6
// }

enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
console.log(Days["Sun"]); // 0
console.log(Days[0]); // 'Sun'

enum Days2 { Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat };
console.log(Days2["Sun"]); // 7

// ➖➖➖➖➖➖➖➖泛型 Generics➖➖➖➖➖➖➖➖➖
// 泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持
// 泛型解决编译期间不确定类型,运行时确定类型
// 只能返回string类型的数据
function getData(value: string): string {
    return value;
}

//同时返回 string类型 和number类型  (代码冗余)
function getData1(value: string): string {
    return value;
}
function getData2(value: number): number {
    return value;
}

// >>>>>>>>>> 使用泛型后就可以解决这个问题
// T表示泛型,具体什么类型是调用这个方法的时候决定的
// 表示参数是什么类型就返回什么类型~~~
function getDataGenerics<T>(value: T): T {
    return value;
}
getDataGenerics<number>(123);
getDataGenerics<string>('1214231');

// 定义接口
interface ConfigFn {
    <T>(value: T): T;
}
var getDatagenr: ConfigFn = function (value) {
    return value;
}
getDatagenr<string>('张三');
getDatagenr<number>(1243);  //错误

// ➖➖➖➖➖➖➖➖类型声明 declare 注解➖➖➖➖➖➖➖➖➖
// 可以通过 declare 关键字,来告诉 TypeScript,你正在试图表述一个其他地方已经存在的代码(如:写在 JavaScript、CoffeeScript 或者是像浏览器和 Node.js 运行环境里的代码):
// .d.ts
declare var React: any;
declare var Vue: any;

// ➖➖➖➖➖➖➖➖类的新保留字 public private protected➖➖➖➖➖➖➖➖➖
// public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
// private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
// protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问

// ➖➖➖➖➖➖➖➖➖类➖➖➖➖➖➖➖➖➖
interface Animal {
    name: string
}

class Animal {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `My name is ${this.name}`;
    }
}

let ani = new Animal('Jack');
console.log(ani.sayHi()); // My name is Jack
// ➖➖➖➖➖➖➖➖➖继承➖➖➖➖➖➖➖➖➖
class Cat extends Animal {
    constructor(name) {
        super(name); // 调用父类的 constructor(name)
        console.log(this.name);
    }
    sayHi() {
        return 'Meow, ' + super.sayHi(); // 调用父类的 sayHi()
    }
}

let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
// ➖➖➖➖➖➖➖➖➖存储器➖➖➖➖➖➖➖➖➖
class AnimalGetSet {
    constructor(name) {
        this.name = name;
    }
    get name() {
        return 'Jack';
    }
    set name(value) {
        console.log('setter: ' + value);
        this.name = value;
    }
}

let aGs = new AnimalGetSet('Kitty'); // setter: Kitty
aGs.name = 'Tom'; // setter: Tom
console.log(aGs.name); // Jack

//➖➖➖➖➖➖➖➖➖静态方法➖➖➖➖➖➖➖➖➖
class AnimalStatic {
    static isAnimal(a) {
        return a instanceof Animal;
    }
}

let aSta = new AnimalStatic();
AnimalStatic.isAnimal(aSta); // true
// 只能通过类名调用
// aSta.isAnimal(aSta); // TypeError: a.isAnimal is not a function ❌

// ➖➖➖➖➖➖➖➖➖抽象类➖➖➖➖➖➖➖➖➖
abstract class AnimalAbstract {
    abstract makeSound(): void
    move(): void {
        console.log('roaming the earch...')
    }
}
// 子类必须实现抽象类的抽象方法

// ➖➖➖➖➖➖➖➖➖implements➖➖➖➖➖➖➖➖➖
interface Point {
    x: number;
    y: number;
}

class MyPoint implements Point {
    x: number;
    y: number; // Same as Point
}

五、vue中使用ts (Vue2.5 + )

六、欣赏一段vue3.0 pre-alpha源码

vue-next/blob/master/packages/reactivity/src/reactive.ts image