TypeScript学习(1)

发布时间 2023-11-30 09:54:50作者: Xianhao

TS基础

基本用法

TypeScript 代码最明显的特征,就是为 JavaScript 变量加上了类型声明。

let foo:string;

变量foo的后面使用冒号,声明了它的类型为string

类型声明的写法,一律为在标识符后面添加“冒号 + 类型”。函数参数和返回值,也是这样来声明类型。

function toString(num:number):string {
  return String(num);
}

函数toString()的参数num的类型是number。参数列表的圆括号后面,声明了返回值的类型是string

变量的值应该与声明的类型一致,如果不一致,TypeScript 就会报错

any 类型

any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值

let x:any;

x = 1; // 正确
x = 'foo'; // 正确
x = true; // 正确

变量x的类型是any,就可以被赋值为任意类型的值。

变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。

实际开发中,any类型主要适用以下两个场合

(1)出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any

(2)为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any,TypeScript 编译时就不会报错。

总之,TypeScript 认为,只要开发者使用了any类型,就表示开发者想要自己来处理这些代码,所以就不对any类型进行任何限制,怎么使用都可以。

TypeScript 提供了一个编译选项noImplicitAny,打开该选项,只要推断出any类型就会报错。

tsc --noImplicitAny app.ts

unknown类型

unknownany的相似之处,在于所有类型的值都可以分配给unknown类型。

let x:unknown;

x = true; // 正确
x = 42; // 正确
x = 'Hello World'; // 正确

unknown类型跟any类型的不同之处在于,它不能直接使用。主要有以下几个限制

  1. 首先,unknown类型的变量,不能直接赋值给其他类型的变量(除了any类型和unknown类型)
  2. 不能直接调用unknown类型变量的方法和属性
  3. unknown类型变量能够进行的运算是有限的,只能进行比较运算(运算符=====!=!==||&&?)、取反运算(运算符!)、typeof运算符和instanceof运算符这几种,其他运算都会报错。

never 类型

不存在任何属于“空类型”的值,该类型被称为never,即不可能有这样的值。

let x:never;

变量x的类型是never,就不可能赋给它任何值,否则都会报错。

八大基础类型

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

boolean类型

boolean类型只包含truefalse两个布尔值。

const x:boolean = true;
const y:boolean = false;

string 类型

string类型包含所有字符串

const x:string = 'hello';
const y:string = `${x} world`;

number 类型

number类型包含所有整数和浮点数

const x:number = 123;
const y:number = 3.14;
const z:number = 0xffff;

bigint 类型

bigint 类型包含所有的大整数。

const x:bigint = 123n;
const y:bigint = 0xffffn;

bigint 与 number 类型不兼容。

const x:bigint = 123; // 报错
const y:bigint = 3.14; // 报错

bigint类型赋值为整数和小数,都会报错。

symbol 类型

暂时用不上

object 类型

object 类型包含了所有对象、数组和函数

const x:object = { foo: 123 };
const y:object = [1, 2, 3];
const z:object = (n:number) => n + 1;

undefined 类型,null 类型

undefined 和 null 是两种独立类型,它们各自都只有一个值。

undefined 类型只包含一个值undefined,表示未定义(即还未给出定义,以后可能会有定义)。

null 类型也只包含一个值null,表示为空(即此处没有值)。

值类型

TypeScript 规定,单个值也是一种类型,称为“值类型”。

let x:'hello';

x = 'hello'; // 正确
x = 'world'; // 报错

变量x的类型是字符串hello,导致它只能赋值为这个字符串,赋值为其他字符串就会报错。

TypeScript 推断类型时,遇到const命令声明的变量,如果代码里面没有注明类型,就会推断该变量是值类型。

// x 的类型是 "https"
const x = 'https';

// y 的类型是 string
const y:string = 'https';

变量xconst命令声明的,TypeScript 就会推断它的类型是值https,而不是string类型。

联合类型

联合类型(union types)指的是多个类型组成的一个新类型,使用符号|表示。

联合类型A|B表示,任何一个类型只要属于AB,就属于联合类型A|B

let x:string|number;

x = 123; // 正确
x = 'abc'; // 正确

上面示例中,变量x就是联合类型string|number,表示它的值既可以是字符串,也可以是数值。

联合类型可以与值类型相结合,表示一个变量的值有若干种可能。

let setting:true|false;

let gender:'male'|'female';

let rainbowColor:'赤'|'橙'|'黄'|'绿'|'青'|'蓝'|'紫';

上面的示例都是由值类型组成的联合类型,非常清晰地表达了变量的取值范围。

type 命令

type命令用来定义一个类型的别名。

type Age = number;

let age:Age = 55;

type命令为number类型定义了一个别名Age。这样就能像使用number一样,使用Age作为类型。

typeof 运算符

typeof 运算符是一个一元运算符,返回一个字符串,代表操作数的类型。

JavaScript 里面,typeof运算符只可能返回八种结果,而且都是字符串。

typeof undefined; // "undefined"
typeof true; // "boolean"
typeof 1337; // "number"
typeof "foo"; // "string"
typeof {}; // "object"
typeof parseInt; // "function"
typeof Symbol(); // "symbol"
typeof 127n // "bigint"

上面示例是typeof运算符在 JavaScript 语言里面,可能返回的八种结果。

TypeScript 将typeof运算符移植到了类型运算,它的操作数依然是一个值,但是返回的不是字符串,而是该值的 TypeScript 类型。

数组

TypeScript 数组有一个根本特征:所有成员的类型必须相同,但是成员数量是不确定的,可以是无限数量的成员,也可以是零成员。

数组的类型有两种写法。第一种写法是在数组成员的类型后面,加上一对方括号。

let arr:number[] = [1, 2, 3];

上面示例中,数组arr的类型是number[],其中number表示数组成员类型是number

如果数组成员的类型比较复杂,可以写在圆括号里面。

let arr:(number|string)[];

上面示例中,数组arr的成员类型是number|string

数组类型的第二种写法是使用 TypeScript 内置的 Array 接口。

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

上面示例中,数组arr的类型是Array<number>,其中number表示成员类型是number

越界访问数组不会报错

由于成员数量可以动态变化,所以 TypeScript 不会对数组边界进行检查,越界访问数组并不会报错。

let arr:number[] = [1, 2, 3];
let foo = arr[3]; // 正确

上面示例中,变量foo的值是一个不存在的数组成员,TypeScript 并不会报错。

只读数组

TypeScript 允许声明只读数组,方法是在数组类型前面加上readonly关键字。

const arr:readonly number[] = [0, 1];

arr[1] = 2; // 报错
arr.push(3); // 报错
delete arr[0]; // 报错

上面示例中,arr是一个只读数组,删除、修改、新增数组成员都会报错。

TypeScript 将readonly number[]number[]视为两种不一样的类型,后者是前者的子类型。

多维数组

TypeScript 使用T[][]的形式,表示二维数组,T是最底层数组成员的类型。

var multi:number[][] =
  [[1,2,3], [23,24,25]];

上面示例中,变量multi的类型是number[][],表示它是一个二维数组,最底层的数组成员类型是number

元组

元组(tuple)是 TypeScript 特有的数据类型,JavaScript 没有单独区分这种类型。它表示成员类型可以自由设置的数组,即数组的各个成员的类型可以不同。

由于成员的类型可以不一样,所以元组必须明确声明每个成员的类型。

const s:[string, string, boolean]
  = ['a', 'b', true];

元组s的前两个成员的类型是string,最后一个成员的类型是boolean

TypeScript 的区分方法就是,成员类型写在方括号里面的就是元组,写在外面的就是数组。

// 数组
let a:number[] = [1];

// 元组
let t:[number] = [1];

函数

函数的类型声明,需要在声明函数时,给出参数的类型和返回值的类型。

export function hello(name: string): number {
  console.log(name);
  return 1;
}

返回值的类型通常可以不写,因为 TypeScript 自己会推断出来。

箭头函数

const repeat = (str: string, times: number): string => {
  return str.repeat(times);
};

可选参数

如果函数的某个参数可以省略,则在参数名后面加问号表示。

function f(x?:number) {
  // ...
}

f(); // OK
f(10); // OK

参数x后面有问号,表示该参数可以省略。

参数名带有问号,表示该参数的类型实际上是原始类型|undefined,它有可能为undefined。比如,上例的x虽然类型声明为number,但是实际上是number|undefined

函数的可选参数只能在参数列表的尾部,跟在必选参数的后面。

参数默认值

设置了默认值的参数,就是可选的。如果不传入该参数,它就会等于默认值。

function createPoint(
  x:number = 0,
  y:number = 0
):[number, number] {
  return [x, y];
}

createPoint() // [0, 0]

可选参数与默认值不能同时使用。

rest 参数

rest 参数表示函数剩余的所有参数,它可以是数组(剩余参数类型相同),也可能是元组(剩余参数类型不同)。

// rest 参数为数组
function joinNumbers(...nums:number[]) {
  // ...
}

// rest 参数为元组
function f(...args:[boolean, number]) {
  // ...
}