likes
comments
collection
share

TypeScript 快速上手指南

作者站长头像
站长
· 阅读数 45

1 基础部分

1.1 环境

npm install ts-node -g
ts-node fileName.ts

npm install esno -g
esno fileName.ts
  • 使用 tsc / tsup 编译 ts
npm install typescript  -g
// 查看 tsc 版本
tsc -v
// 编译 ts 文件
tsc fileName.ts

esno 和 tsup 都是基于 esbuild 开发的

1.2 类型基础

JavaScript 的原始数据类型:

  • boolean / number / string / symbol / bigint
  • undefined / null

undefined 和 null 是所有类型的子类型,可赋值给其他类型;如果指定了 --strictNullChecks 标记,null 和 undefined 只能赋值给 void 和它们自己。

let isDone: boolean = false;
let age: number = 10;
let str: string = "test";

let u: undefined = undefined;
let n: null = null;

// 非--strictNullChecks下
let num: number = undefined;

TS 的特殊类型:

  • any:回到了 JS,一般不会用
  • unknown:不知道用什么类型声明变量时使用,但操作变量时需要收缩类型范围,否则会报错(any不会报错)。
  • void:单独声明一个 void 类型的变量没有什么大用,因为你只能为它赋值 undefined / null,函数没有返回值时其返回的类型为 void。
  • never:表示一个函数永远没有返回值(表现为抛出异常或无法执行到函数结束),never 类型是任何类型的子类型
function test(x: unknown) {
  if (typeof x === "string") {
    return x.toUpperCase();
  }
}

function error(message: string): never {
    throw new Error(message);
}
  • 数组:两种定义方法:使用[]定义和使用Array泛型,推荐使用前者
let arr1: number[] = [1, 2, 3];

let arr2: Array<number> = [1, 2, 3];
  • 元组:元组类型是一种特殊的数组类型,它确切地知道元素数量及其特定位置的类型。
let x: [string, number];

x = ['hello', 10]; // OK
x = [10, 'hello']; // Error
  • 枚举:用于常量映射
// 默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数
enum Direction {
  Up, // 相当于 = 0
  Down,
  Left,
  Right,
}
console.log(Direction.Up) // 0
// 反向映射
console.log(Direction[0])

// 如果枚举第一个元素赋有初始值,就会从初始值开始递增
enum Direction {
    Up = 6,
    Down,
    Left,
    Right
}

// 字符串枚举
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
console.log(Direction.Up); // UP

1.3 interface 和 type

interface:接口,一般用于描述对象和类以及函数

interface Person {
  name: string;
  age?: number; // 可选
  readonly address: string; // 只读
}

// 函数
interface SearchFunc {
  (source: string): boolean;
}

interface Shape {
  color: string;
}

// 接口继承接口
interface Square extends Shape {
  length: number;
}

type:类型别名,type 可以声明基本类型别名,联合类型,交叉类型,元组等类型

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;

type Shape = { color: string; };
type Square = Shape & { length: number; }; // 相当于interface的extends

// 函数
type SearchFunc = (source: string) => boolean;

区别和联系:

  • interface 是一种数据结构的描述(shape),比如对象和类,interface 可以多次定义并能够合并,可以被 extends 和 implements
  • type 是类型别名,是一种表达式;type 可以声明基本类型别名,联合类型,交叉类型、元组等类型
  • 用 interface 描述数据结构,用 type 描述类型关系;能用 interface 实现,就用 interface , 如果不能就用 type
  • 两者都可以用来描述对象或函数的类型

1.4 类型工具

  • 类型推导:初始化变量和成员,设置函数的默认值和决定返回值时,TS 会自带推导类型。
const hello = "hello typescipt!"; // 自动推导为 string 类型

// 自动推导 函数的参数为 string 类型和返回类型为 void
function sayHello(str = "test") {
  console.log(str);
}
  • 联合类型:表示一个值可以是几种类型之一,交集
let numberOrString: number | string 
// 注意只能使用 联合类型的所有类型里 共有的属性或方法:
numberOrString.length
numberOrString.toString()
  • 交叉类型:将多个类型合并为一个类型,并集
interface IName  {
  name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'hello', age: 12}
  • 字面量类型:提供一个准确的变量值,目前支持 字符串、数字、布尔
type Direction = 'North' | 'East' | 'South' | 'West';
  • 类型断言:告诉编译器你比它更了解这个类型,小心使用
let value: unknown = "this is a string";

let length1: number = (<string>value).length; // jsx语法会有歧义

let length2: number = (value as string).length; // 推荐使用 as
  • 非空断言:告诉编译器变量一定不是 nullundefined小心使用
let flag: null | undefined | string;
flag!.toString(); // ok
flag.toString(); // error
  • 类型守卫:是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。
// 在条件语句中使用 typeof/instanceof/in 收缩类型范围,TS 会自动推导类型
function getLength2(input: string | number): number {
  if (typeof input === 'string') {
    return input.length
  } else {
    return input.toString().length
  }
}

// 将判断逻辑封装起来提取到函数外部进行复用, 使用 is
function isString(input: unknown): input is string {
  return typeof input === "string";
}

1.5 类与继承

TS 支持了修饰符:

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
interface Radio {
  switchRadio(trigger: boolean): void;
}
// 类实现接口
class Car implements Radio {
  switchRadio(trigger) {
    return 123
  }
}

class Person {
  public name: string;
  protected age: number;
  private tel: number;

  constructor(name: string, age: number, tel: number) {
    this.name = name;
    this.age = age;
    this.tel = tel;
  }
}

// 继承
class Child extends Person {
  constructor(name: string, age: number, tel: number) {
    super(name, age, tel);
  }

  getName() {
    console.log(`我的名字叫${this.name},年龄是${this.age}`);
  }
}

  • 抽象类:只能被继承,但不能被实例化的类
abstract class Animal {
    constructor(name:string) {
        this.name = name
    }
    public name: string
    public abstract sayHi():void
}

class Dog extends Animal {
    constructor(name:string) {
        super(name)
    }
    public sayHi() {
        console.log('wang')
    }
}

1.6 泛型

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

function echo<T>(arg: T): T {
  return arg
}

// 多个泛型
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]]
}

// 继承类型
function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length)
  return arg
}

//泛型和 interface
interface KeyPair<T, U> {
  key: T;
  value: U;
}
//泛型和 interface
interface KeyPair<T, U> {
  key: T;
  value: U;
}
const kp1: KeyPair<number, string> = { key: 123, value: "str" };
const kp2: KeyPair<string, number> = { key: "str", value: 123 };

2 工程部分

2.1 tsconfig

通过 tsc --init 初始化,生成 tsconfig.json 文件;配置文档

顶层选项:

  • compilerOptions:编译配置
  • files / extends / include /exclude / references
  • watchOptions
  • typeAcquisition

2.2 类型声明

  • d.ts 类型声明文件: 一般来说 ts 会解析所有的.ts.d.ts文件
  • declare:手动声明
declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

2.3 命令空间

命名空间是TS提供隔离手段,防止命名冲突;一般来说使用模块化足够了,

  • 使用 export 向外暴露
  • import 定义别名
namespace Vehicle {
  const name = "Toyota"
  // 暴露
  export function getName () {
      return `${name}`
  }
}

//允许嵌套    
namespace TransportMeans {  
  export namespace Vehicle {
    const name = "Toyota"
    export function getName () {
        return `${name}`
    }
  }
}
// 别名
import carName= TransporMeans.Vehicle;

2.4 三斜线指令

只能放在文件最顶端,告诉编译器在编译过程需要引入的文件。

/// <reference types="node" />   // 表示库的类型声明 如@types/node/index.d.ts 

/// <reference path="..." />  // 引入文件

3 类型进阶

3.1 索引类型

支持字符串和数字两种索引签名,数字索引的返回值必须是字符串索引返回值类型的子类型。JavaScript 中,数字索引访问会自动转换为字符串索引访问。

interface AllStringTypes {
  [key: string]: string;
}

// 将对象中的所有键转换为对应字面量类型
interface Foo {
  PropA: string;
  PropB: number;
}
type FooKeys = keyof Foo; // 字面量类型 "PropA"|"PropB"

type PropAType = Foo[PropA]; 

type PropTypeUnion = Foo[keyof Foo]; // 联合类型 string | number
  • 类型映射
type Clone<T> = {
  [K in keyof T]: T[K];
};

3.2 内置工具

Partial

type Partial<T> = {
    [P in keyof T]?: T[P];
};


interface ApiKey {
  id: number;
  name: string;
}

const dataType1: ApiKey = {
  id: 1,
  name: 'static'
}

const dataType2:  Partial<ApiKey> = {
  name: 'json'
}

Record

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

type Coord = Record<'x' | 'y', number>;

// 等同于
type Coord = {
	x: number;
	y: number;
}

Readonly

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

type Coord = Readonly<Record<'x' | 'y', number>>;

// 等同于
type Coord = {
    readonly x: number;
    readonly y: number;
}

Pick

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

type Coord = Record<'x' | 'y', number>;
type CoordX = Pick<Coord, 'x'>;

// 等用于
type CoordX = {
	x: number;
}

Omit

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

interface I1 {
	a: number;
	b: string;
	c: boolean;
}

type AC = Omit<I1, 'b'>;     // { a:number; c:boolean } 
type C = Omit<I1, 'a' |'b'>  // { c: boolean }

infer

infer可以在extends的条件语句中推断待推断的类型

infer R 的位置代表了一个未知的类型,可以理解为在条件类型中给了它一个占位符,然后就可以在后面的三元运算符中使用它。

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type func = () => number;
type variable = string;
type funcReturnType = ReturnType<func>; // funcReturnType 类型为 number
type varReturnType = ReturnType<variable>; // varReturnType 类型为 any


type Unpack<T> = T extends Array<infer R> ? R : T

type NumArr = Array<number>

type U = Unpack<NumArr> // number

参考

转载自:https://juejin.cn/post/7282603912649998336
评论
请登录