Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript相关 #7

Open
buddywang opened this issue Sep 17, 2020 · 2 comments
Open

TypeScript相关 #7

buddywang opened this issue Sep 17, 2020 · 2 comments

Comments

@buddywang
Copy link
Owner

buddywang commented Sep 17, 2020

是什么和为什么

TypeScript 是 JavaScript 多一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发。用 TypeScript 具有不仅于以下好处:

  • 增加代码多可读性和可维护性
  • 可以在编译阶段就发现大部分错误
  • 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等

常用用法

为变量声明类型

let t1: boolean = false;
let t2: number = 1;
let t3: string = 'asdf';
let t4: null = null;
let t5: undefined = undefined;
// any 表示任何类型,对该类型值的任何操作会逃过类型检查,尽量不要用,需要时用 unknown 类型替代
let t6: any; 
// 对照于any,unknown 是类型安全的。 任何值都可以赋给 unknown,但是当没有类型断言或基于控制流的类型细化时 unknown 不可以赋值给其它类型
let t7: unknown; 

// 联合类型,当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
let t8: string | number;
t8. length // length 不是 string 和 number 的共有属性,报错
if(typeof t8 === 'string'){
  t8.length; // 不会报错
}

// 定义函数,定义参数类型和返回值类型
function t9(a: number = 1, b?: number): void { // ? 表示可选参数,带默认值的参数 ts 也认为其是可选参数,void 表示函数没有返回值
  ...
}
// 函数表达式的定义
t9: (a: number, b?: number) => void = function (a: number, b?: number) {
  ...
}
// 函数重载(可以用泛型实现)
function t9(x: number): number;
function t9(x: string): string;
function t9(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

// 定义数组
let t10: number[] = [1, 2, 4];
  // 或者用数组泛型
let t11: Array<number> = [1, 2, 3];

// 定义对象的类型,用 interface 描述对象的形状
interface It12 { // 建议接口的名称加上 I 前缀
  readonly id: number; // readonly 定义只读属性
  name: string;
  age?: number; // 可选属性
  [propName: string]: any; // 任意属性,确定属性和可选属性的类型都必须是它的类型的子集,一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
}
// 赋值的时候,变量的形状必须和接口的形状保持一致,否则报错
let t13: It12 = { // 缺少属性,报错
  name: 'tom',
}

// 枚举,枚举成员会被赋值为从 0 开始递增的数字,Days.Sun = 0
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

类型断言

手动指定一个值的类型,语法是

值 as 类型
用途:

  • 将一个联合类型断言为其中一个类型
  • 将一个父类断言为更加具体的子类
  • 将任何一个类型断言为 any,慎用
window.foo = 1 // 报错
(window as any).foo = 1 // ok
  • 将 any 断言为一个具体的类型
  • type
// 类型别名,用来给一个类型起个新名字
type Name = string;
let a: Name = 'sss';

// 字符串字面量类型,约束取值范围
type EventNames = 'click' | 'scroll' | 'mousemove';
let event: EventNames; // event 只能取 'click' | 'scroll' | 'mousemove' 其中之一

类与接口

interface 也可以用来对类的一部分行为进行抽象,然后类可以实现(implements)接口,例如,对于防盗门和车,都可以有报警器多功能,这时可以把报警器提取出来,作为一个接口,防盗门和车都去实现它:

interface Alarm {
    alert(): void;
}

class Door {
}

class SecurityDoor extends Door implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}

class Car implements Alarm {
    alert() {
        console.log('Car alert');
    }
}

注意:
类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类,例如:

  • 一个类可以实现多个接口
  • 接口之间可以继承
  • 接口可以继承类
class Point {
    x: number;
    y: number;
}

// Point 类定义创建了 Point 类型
interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

// 函数使用泛型
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}
swap<number, string>([7, 'seven']); // ['seven', 7]
  // 也可以不指定<T, U>,而让类型推论自动推算出来
swap([7, 'seven']); // ['seven', 7]
// 泛型约束:对泛型使用 extends,可以对泛型进行约束
interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.

// 接口使用泛型
interface CreateArrayFunc<T> {
    (length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']

// 类使用泛型
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

声明文件

参考

@buddywang
Copy link
Owner Author

buddywang commented Nov 18, 2020

快捷外部模块声明

当你使用一个新模块时,如果不想要花费时间书写一个声明时,现在你可以使用快捷声明以便以快速开始。

declare module "hot-new-module";

现在你可以导入 hot-new-module 使用了,所有从快捷模块的导入都具有 any 类型。

import x, {y} from "hot-new-module";
x(y);

模块名称中的通配符

ts 不支持导入非 js 资源,要导入图片,json等资源要借助 webpack 等的 loader,那在 ts 里为了不报错要为这类模块定义一个外部模块声明。
TypeScript 2.0支持使用通配符符号(*)定义一类模块名称。这种方式,一个声明只需要一次扩展名,而不再是每一个资源。

declare module "*.json";
declare module "*.png";
...

现在,你可以导入非 js 资源了

import demoPng from './demo.png'

@buddywang
Copy link
Owner Author

buddywang commented Nov 19, 2020

keyof 索引类型查询操作符

keyof T, 对于任何类型 T, keyof T的结果为 T上已知的公共属性名的联合(动态的联合),例如:

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

let key: keyof Person // 'name' | 'age'

常见用法

  • 解决用 [key] 访问对象属性时报错
interface Person {
  name: string;
  age: number;
}

let p: Person = {
  name: 'ss',
  age: 33
}
let key = 'name'
// p[key] = 'dd' // Error: Element implicitly has an 'any' type because type 'Person' has no index signature.
p[key as keyof Person] = 'dd'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant