TypeScript快速入门

November 18, 2023 作者: yijianhao 分类: 前端 浏览: 125 评论: 0

TypeScript 快速入门

TypeScript 简称 TS, 是JS(JavaScript)的超集,TS编译器会把TS代码编译成JS代码;为微软家开发的;

安装

  1. 全局安装`sudo npm i -g typescript`

  2. 安装好后输入命令`tsc -v`可以查看输出结果

  3. 编译,输入`tsc index.ts`, 会将index.ts文件转编译成index.js文件

初始化配置文件

首先需要创建一个项目文件夹,在命令行中`cd`到文件夹后执行`tsc --init`, tsc就会在文件夹中创建一个默认的TS配置文件`tsconfig.json`,刚开始建议可以修改以下的值:

{
	"target": "ES2016",
	"rootDir": "./src",  // 将源码默认存放的文件夹放到src目录下
	"outDir": "./dist",  // 将编译后生成的代码放到dist目录下
	"noEmitOnError": true, // 编译出错后不继续编译
	"sourceMap": true,     // 编译后生成一个sourceMap文件,开发的时候方便浏览器调试,生产环境可以关掉
	... ...
}

有了`tsconfig.json`文件后直接输入`tsc`命令后就会按照配置进行编译项目

类型

TS变量标记类型的方式:

let t: number; // 不含初始值
const x: string = '含初始化值';
const a: number[] = [1, 2, 3];  // 数组

// 如果没有给变量标记类型,TS默认有类型推断的功能
const q = 123; // number

JS有6种类型, TS有5种新增类型:

JS

注意

TS

number

数字

ts中的nubmer类型可以用下划线分隔,

any

任意

string

字符串

unknown

未知

boolean

布尔

never

null

enum

枚举

undefined

未定义

tuple

元组

objects

对象

any、unknown、never的使用区别

  1. any:这个类型是 TypeScript 的超级类型(也可以说是 "万能" 类型)。如果声明一个值为 any 类型,那么可以对它进行任何操作,而不会看到类型错误,这个类型一般不能随意使用,因为其实它就是回归了JS的用法。

  2. unknown:这个类型是 TypeScript 的安全类型版本的 any。如果声明一个值为 unknown 类型,那么不能对它进行任何操作,除非先进行类型检查或类型断言。例如:

let unknownThing: unknown = 'hello';
unknownThing = 42;
unknownThing.foo.bar;  // 会报错

if (typeof unknownThing === 'object' && unknownThing != null) {
    unknownThing.foo.bar;  // 不会报错,因为已进行了类型检查
}
  1. never:这个类型表示永远不会有值的类型。例如,函数如果永远抛出错误或者永远循环,那么它的返回类型就是 never。

function alwaysThrows(): never {
    throw new Error('Always throws');
}

function infiniteLoop(): never {
    while (true) {}
}

数组

在TS中,有两种定义数组的方式:

  1. 类型 + []

let numberArray: number[] = [1, 2, 3];
let stringArray: string[] = ['hello', 'world'];
  1. Array<elemType>

let numberArray: Array<number> = [1, 2, 3];
let stringArray: Array<string> = ['hello', 'world'];
let arrayArray: Array<number[]> = [[1], [2, 3]];

ts中有一种称为元组的特殊数组⬇️

元组(Tuple)

元组是一个已知元素数量和类型的数组,各元素的类型不必相同。

const x: [string, number] = ['s', 12]

枚举(enum)

enum Size {
    Small,
    Medium,
    Large,
    Error = -1
}
const myClothSize: Size = Size.Large   

函数(function)

在 TypeScript 中,函数的类型由参数类型和返回值类型组成。函数默认返回一个`undefined`类型,并且会自动推断函数返回值:

function greet(name: string): string {
    return 'Hello, ' + name;
}

function greet1(name: string) {  // 自动推断类型为string
    return 'Hello1, ' + name;
}

function print(name?: string): void {   // ?: 表示参数可选
    if (name) {
        console.log('Hello, ' + name);
    } else {
        console.log('Hello, Guest');
    }
}

function greet2(name: string = 'Guest'): string {  // = 表示参数有默认值,参数亦可选
    return 'Hello, ' + name;
}

TS函数检查配置

在`tsconfig.json`文件中可以设置以下几个值对函数进行规范检查:

{
   ...
   "noUnusedLocals": true,  // 没有用到的函数变量会被警告
   "noUnusedParameters": true,  // 函数参数没有被用到就会引发错误
}

函数重载

TS函数支持函数重载:

function add(a: number, b: number): number;
function add(a: string, b: string): string;

function add(a: any, b: any): any {
    if (typeof a === 'string' && typeof b === 'string') {
        return a + b;  // 字符串拼接
    } else if (typeof a === 'number' && typeof b === 'number') {
        return a + b;  // 数值相加
    }
}

objects

在 TypeScript 中,你可以使用接口(`interface`)或类型别名(`type`)来描述一个对象的形状(shape)。

1. 接口 interface

接口是定义对象的一个强有力的方式。可以定义必需的属性、可选的属性,以及索引签名等。例如:

interface Person {
    firstName: string;
    lastName: string;
    age?: number;  // 可选属性
    [propName: string]: any;  // 索引签名

}

let john: Person = {
    firstName: 'John',
    lastName: 'Doe',
    age: 27,
    address: '123 Main St'  // 任意额外属性都是允许的,因为有索引签名
};

2. 类型别名 type

类型别名也可以用来描述一个对象的形状,且可以表示更复杂的类型形状:

type Person = {
    firstName: string;
    lastName: string;
    age?: number;
}

let jane: Person = {
    firstName: 'Jane',
    lastName: 'Doe',
    age: 25
};

接口和类型别名在大多数情况下可以互换使用,但它们有一些微妙的差别。例如,接口可以被声明合并(同名接口在编译时会被合并为一个接口),而类型别名不能。相反,类型别名可以表示一些接口不能表示的类型,例如联合类型。

此外,TypeScript 还支持类(`class`)和字面量对象(literal object),这些都可以用来创建和使用对象。例如:

class Car {
    make: string;
    model: string;
    constructor(make: string, model: string) {
        this.make = make;
        this.model = model;
    }
    drive() {
        console.log(`Driving a ${this.make} ${this.model}`);
    }
}
let myCar = new Car('Toyota', 'Corolla');
myCar.drive();
let myObject = {
    prop1: 'Hello',
    prop2: 42
};

3. 接口和别名的区别

  1. 接口可以被重复声明,重复的声明会被合并;

  2. 接口可以被类实现(implements)

  3. 接口只能用来描述对象的形状(包括函数和构造签名)

  4. 类型别名不能被声明合并。声明一个类型别名会报错。

  5. 类型别名也可以使用 typeof 关键字来获取一个值的类型。

  6. 类型别名可以表示一些接口不能表示的类型形状,例如原始类型(如 string,number),元组,和联合类型

联合类型 Union Types

联合类型是一种复合类型,表示一个值可以是几种类型之一, 用 |管道符 表示。

let myVar: string | number;

myVar = 'Hello';  // OK
myVar = 42;       // OK
myVar = true;     // Error: 


function processValue(value: string | number) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    } else {
        return value.toFixed(2);
    }
}

交叉类型 Intersection Types

交叉类型是一种复合类型,表示一个值同时具备多种类型的特性。你可以通过 & 与操作符将多个类型组合成一个交叉类型。

interface CanWalk {
    walk: () => void;
}

interface CanSwim {
    swim: () => void;
}

type Amphibian = CanWalk & CanSwim;

let frog: Amphibian = {
    walk: () => console.log('Walking'),
    swim: () => console.log('Swimming')
};

字面量类型 Literal Types

字面量类型和枚举(enum)在某些场景下可以起到类似的作用

type Easing = "ease-in" | "ease-out" | "ease-in-out";  // 字符串字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;                 // 数字字面量类型
type TrueOnly = true;                                  // 布尔字面量类型 不常用

Nullable TypesNullable Types

Nullable Types

在 TypeScript默认情况中,null 和 undefined 是所有其他类型的子类型,这意味着可以将 null 和 undefined 赋值给任何类型的变量。

tsconfig.json 文件中设置 "strictNullChecks": true可以改变上面的默认行为,启用 strictNullChecks 之后,null 和 undefined 就只能被赋值给它们各自和 void 的类型,如果想让其他类型的变量能被赋值 null 或 undefined,你就需要使用联合类型。

let s: string;     // OK
s = null;          // Error when strictNullChecks is true

let sn: string | null;  // OK
sn = null;         // OK

sn = "abc";        // OK

映射类型 Mapped types

从一个已知的类型生成新的类型,主要的功能就是通过一个已知的类型 T,生成一个新的类型,其中 T 中的每个属性都会进行某种形式的转换

  1. 改变属性的可选性:比如 Partial<T> 和 Required<T>,它们分别可以将 T 中的所有属性变为可选的或必须的。

type Partial<T> = {
    [P in keyof T]?: T[P];   //  ? 表示添加可选性
};

type Required<T> = {
    [P in keyof T]-?: T[P];   // -? 表示去除可选性
};

  1. 添加 readonly 修饰符:比如 Readonly<T>,它可以将 T 中的所有属性变为只读的。

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
  1. 将属性的值的类型进行转换:自定义映射类型,将属性的值的类型进行转换。

type Stringify<T> = {
    [P in keyof T]: string;
};

条件类型 Conditional types

允许一个类型表达式根据一个条件选择两种可能的类型之一。通过 T extends U ? X : Y 的形式实现。

type IfArray<T> = T extends Array<any> ? T[number] : T;

type A = IfArray<string>;  // string
type B = IfArray<string[]>;  // string
// 在这个例子中,IfArray<T> 是一个条件类型,它根据类型 T 是否可以赋值给 Array<any> 来选择不同的类型。如果 T 可以赋值给 Array<any>,那,也么结果类型就是 T[number]就是数组的元素类型;否则结果类型就是 T。

直接看例子:

  1. 提取函数的返回类型

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

function f() {
   return { a: 1, b: 2 };
}

type R = ReturnType<typeof f>; // { a: number; b: number; }
  1. 判断一个类型是否是数组

type IsArray<T> = T extends Array<any> ? true : false;

type A = IsArray<number[]>;  // true
type B = IsArray<number>;  // false
  1. 提取 Promise 的结果类型

type PromiseType<T> = T extends Promise<infer U> ? U : T;

async function g() {
   return { a: 1, b: 2 };
}

type P = PromiseType<ReturnType<typeof g>>;  // { a: number; b: number; }

TS 中的面向对象

在 TypeScript 中,可以使用类(class)、接口(interface)、继承(inheritance)、封装(encapsulation)等概念来实现面向对象编程。

类 class

类(Class): 类是面向对象编程的核心,它定义了一件事物的抽象特点。类定义了事物的属性(fields)和方法(methods)。

class Car {
  color: string;
  constructor(color: string) {
    this.color = color;
  }
  drive() {
    console.log('Driving...');
  }
}

对象 Object

类的实例被称为对象。可以使用 new 关键字创建一个新的对象实例。

继承 Inheritance

class Tesla extends Car {
  autopilot: boolean;

  constructor(color: string, autopilot: boolean) {
    super(color); // call the constructor of the base class
    this.autopilot = autopilot;
  }

  selfDrive() {
    if (this.autopilot) {
      console.log('Self-driving...');
    }
  }
}

let myTesla = new Tesla('white', true);
myTesla.selfDrive();

封装 Encapsulation

可以使用 private、protected 和 public 访问修饰符来实现封装

  • public: 公开修饰(默认),类外部可以访问

  • privite: 私有修饰,只有类内部被允许访问

  • protected:保护修饰,类内部和派生类可以访问

接口 Interface

接口在 TypeScript 中是一个非常重要的概念。接口定义了代码中对象的类型,描述了对象的形状和方法

interface Drivable {
  drive(): void;
}

class Car implements Drivable {  // 实现
  drive() {
    console.log('Driving...');
  }
}

声明文件

// index.d.ts
declare function myFunction(a: string, b: number): void;

... 等我研究一下

#前端(1)

评论