TypeScript学习 (一)

133 阅读9分钟

TypeScript(一)

本文摘录自:公众号:深圳湾码农

一、介绍

1、什么是ts

    TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。

简而言之,TypeScript是JavaScript的超集,具有可选的类型并可以编译为纯JavaScript。从技术上讲TypeScript就是具有静态类型的 JavaScript 。

2、ts优缺点

优点

增强代码的可维护性,尤其在大型项目的时候效果显著;

友好地在编辑器提示错误,编译阶段就能检查类型发现大部分错误;

支持最新的JavaScript新特性;

周边生态繁荣,vue3已经全面支持ts;

缺点

需要一定的学习成本;

和一些插件库的兼容并不是特别完美,如以前在vue2项目里使用typescript就并不是那么顺畅;

增加前期开发的成本,毕竟需要编写更多的代码(但是便于后期维护)

3、安装环境 & DEMO

安装环境

npm install -g  typescript // 全局安装 ts
tsc -v    // 验证是否安装过ts & 查看已安装ts的版本
Version 4.7.4    // 出现版本号 说明已经安装成功
tsc --init    // 生成tsconfig.json配置文件

执行上面的命令我们可以看到生成了一个tsconfig.json文件,里面有一些配置信息。

1、创建 helloworld.ts命令,目录下生成了一个同名的 helloworld.ts 文件,代码如下:

const str: string = 'this is string';
log(str)

2、控制台执行 tsc helloworld.ts命令,目录下生成一个同名的 helloworld.js 文件,代码如下:

var str = 'this is string';
log(str)        // 通过tsc命令我们发现ts的代码内转换成了js代码

3、使用 node 执行 js 代码,即可查看结果。

node helloworld.js

二、基本类型

1、Boolean类型

const isDone: boolean = false;

2、Number类型

const age: number = 20;

3、String类型

let str: string = 'str'

4、Enum类型

枚举类型用于定义数值集合,使用枚举类型我们可以定义一些带名字的常量。使用枚举可以清晰地表达意图或创建一组有区别的用例。如:周一到周日,方位上下左右等

1、普通枚举

初始值默认为0,其余的成员会按顺序自动增长。可以理解为数组下标

enum Color {
    RED,
    PINK,
    BLUE,
}

const red: Color = Color.RED;
log(red);    // 0

2、设置初始值

enum Color {
  RED = 2,
  PINK,
  BLUE,
}

const blue: Color = Color.BLUE;
console.log(blue);  // 4

3、字符串枚举

enum Color {
  RED = '红色',
  PINK = '粉色',
  BLUE = '蓝色',
}

const pink: Color = Color.PINK;
console.log(pink);  // 粉色

4、常量枚举

使用 const 关键字修饰的枚举即为常量枚举,常量枚举与普通枚举的区别是:常量枚举会在编译阶段被删除,看下编译之后的效果:

const enum Color {
  RED,
  PINK,
  BLUE,
}

const color: Color[] = [Color.RED, Color.PINK, Color.BLUE];
console.log(color); 

// 可以看到我们的枚举并没有被编译成js代码,只是把 color 这个数组变量编译出来了
var color = [0 /* Color.RED */, 1 /* Color.PINK */, 2 /* Color.BLUE */];
console.log(color);

5、Array类型

对数组类型的定义有两种方式:

const arr: number[] = [1,2,3];
const arr2: Array<number> = [1,2,3];

6、元组(tuple)类型

    上面数组类型的方式,只能定义出内部全为同种类型的数组。对于内部不同类型的数组可以使用元组类型来定义:

    元组(Tuple)表示一个已知数量和类型的数组,可以理解为它是一种特殊的数组。

const tuple: [number, string] = [1, '1'];

需要注意的是:元组类型只能表示一个已知元素数量和类型的数组,长度已指定,越界访问会提示错误。例如:一个数组可能有多种类型,数量和类型都不确定,那就直接any[ ]。

7、undefined 和 null

    默认情况下 null 和 undefined是所有类型的子类型。也就是你可以把 null 和 undefined赋值给其它类型。

let a: undefined = undefined;
let b: null = null;

let str: string = 'str';
str = null;        // 编译正确
str = undefined;    //编译正确

如果你在 tsconfig.json 制定了 "strictNullChecks":true的情况下,即开启严格模式: null 和 undefined只能给它们自己的类型赋值。

// 启用 -- strictNullChecks
let x: number;
x = 1;    // 编译正确
x = null;    // 编译错误
x = undefined;    // 编译错误

但是 undefined 可以给 void赋值

let c: void = undefined;    // 编译正确
let d: void = null;     //编译错误

8、void类型

void意思就是无效的,一般只用在函数上,告诉别人这个函数没有返回值。

function sayHello(): void {
    console.log('say hello.');
}

    void类型的用法,主要是用在我们不希望调用者关心函数返回值的情况下,比如通常的异步回调函数

    void也可以定义 undefined 和 null类型(注意上面说的严格模式)

let u: void = undefined
let n: void = null;
  1. 默认情况下,null 和 undefined 是所有类型的子类型;
  2. 严格模式下:null 和 undefined 只能给它们自己类型赋值;
  3. 默认情况下:null 和 undefined 可以给void赋值;
  4. 严格模式下:undefined可以给void赋值,null不可以给void赋值;

9、any类型

any类型会跳过类型检查器对值的检查,任何值都可以赋值给any类型。

let value: any = 1;
value = 'str'; // 编译正确
value = [];    // 编译正确
value = {};    // 编译正确

10、never类型

    never类型表示的是那些永远不存在的值的类型。例如never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的范湖值类型。

    值会用不存在的两种情况:

① 如果一个函数执行时抛出了异常,那么这个函数永远不会存在返回值(因为抛出异常会直接中断程序运行,这使得程序运行不到返回值那一步,即具有不可达的终点,也就永不存在返回了)

② 函数中执行无限循环的代码(死循环),使得程序永远无法运行到函数返回值那一步,永不存在返回。

// 异常
function error(msg: string): never {
  throw new Error(msg);
}

// 死循环
function loopForever(): never {
  while(true) {};
}

11、Unknown类型

    unknown 与 any一样,所有类型都可以分配给 unknown:

     unknow类型比any更加严格当你要使用any 的时候可以尝试使用unknow。

let value: unknown = 1;
value = "string"; //编译正确
value = false;    // 编译正确

unknown 与 any 的最大区别是:

任何类型的值可以赋值给 any,同时any类型的值也可以赋值给任何类型。

unknown 任何类型的值都可以赋值给它,但它只能赋值给unknown和any。(这句话有待考证,目前我无法用代码实现出来。)

三、对象类型

这里所说的对象类型,就是我们常说的函数、{}、数组、类

1、Object、object 和 { } 类型

object类型

    object类型用于表示所有的非原始类型,即我们不能把 number、string、boolean、symbol等原始类型赋值给object。在严格模式下:null 和 undefined类型也不能赋值给 object。

let obj: object;

//  "strict": false
obj = 123; // 编译报错
obj = ""; // 编译报错
obj = false; // 编译报错
obj = Symbol(1); // 编译报错

obj = null; // 编译 正确
obj = undefined; // 编译 正确

//  "strict": true
obj = null; // 严格模式下编译报错
obj = undefined; // 严格模式下编译报错

obj = {}  // 任何模式下都是正确的

Object类型

    大 Object 代表所有拥有 toString、hasOwnProperty方法的类型,所有原始类型、非原始类型都可以赋给 Object(严格模式下 null 和 undefined 不可以

let bigObject: Object = {};

//  "strict": false
bigObject = 1;        //编译正确
bigObject = '';       //编译正确
bigObject = false;    //编译正确
bigObject = null;     //编译正确
bigObject = undefined; //编译正确

//  "strict": true
bigObject = null;      // 严格模式编译错误
bigObject = undefined; // 严格模式编译错误

bigObject = {};

{ }类型

{ }空对象类型和 Object一样,也是表示原始类型和非原始类型的集合。

let a1: {} = {name:1} //正确
let a2: {} =  () => 123//正确
let a3: {} = 123//正确

2、类

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

class Person {
  name: string;
  age: number;

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

  sayHi(): void {
    console.log(`你好, ${this.name},我今年${this.age}岁了。`);
  }
}

let person: Person = new Person("jack", 20);
person.sayHi();

3、数组

const flag1: number[] = [1, 2, 3];
const flags2: Array<number> = [1, 2, 3];

4、函数

函数声明

function add(x: number, y: number) {
    return x + y;
}

函数表达式

const add = function add(x: number, y: number) {
    return x + y;
}

接口定义函数

interface Add {
    (x: number, y: number): number;
}

可选参数

function add(x: number, y?: number): number {
    return y ? x + y : x;
}

默认参数

function add(x: number, y: number = 0): number {
    return x + y;
}

剩余参数

function add(...numbers: number[]): number {
  let sum = 0;
  for(let i = 0; i < numbers.length; i++) {
      sum += numbers[i];
  }
  return sum;
}

let res = add(1,2,3)
console.log(res);

函数重载

函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。

function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
  return x + y;
}

上面示例中,我们给同一个函数提供多个函数类型定义,从而实现函数的重载。

需要注意的是:

函数重载真正执行的是同名函数最后定义的函数体,在最后一个函数体定义之前全部属于函数类型定义,不能写具体的函数实现方法,只能定义类型。

四、类型推断

如果没有明确的指定类型,那么 TypeScript 会依照类型推论规则推断出一个类型。

let x = 1;
x = true;    // 报错

上面代码等价于

let x: number = 1;
x = true; //报错

通过上面的示例可以看出,我们没有给x指定明确类型的时候, typescript会推断出 x 的类型是 number。

而如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:

let x;
x = 1;    // 编译正确
x = true; // 编译正确

五、类型断言

    某种情况下,我们可能比 typescript 更加清楚的知道某个变量的类型,所以我们可能希望手动指定一个值的类型。

类型断言有两种方式:

1、尖括号写法

2、as 写法

// 尖括号写法
let str: any = "this is str";
let strLength: number = (<string>str).length;

// as 写法
let str: any = "this is str";
let strLength: number = (str as string).length;

1、非空断言

    在上下文中当类型检查器无法断定类型时,可以使用缀表达式操作符 !进行断言操作对象是非 null 和非 undefined 的类型,即 x! 的值不会为 null 或 undefined

let user: string | null | undefined;
console.log(user!.toUpperCase());    // 编译正确
console.log(user.toUpperCase());    // 编译错误  实际实验情况下,非严格模式没报错

非严格模式下没报错

2、确定赋值断言

let value: number;
console.log(value)   // Variable 'value' is used before being assigned.

我们定义了变量,没有赋值就使用了,则会报错。非严格模式下没报错

通过 let x!:number;确定赋值断言,TypeScript 编译器就会知道该属性会被明确地赋值。

let value!: number;
console.log(value);    // undefined编译正确