跳到主要内容

TS 基本类型

示例代码:typescript-demos

为什么要用 TypeScript?

好处:

  1. 规避大量低级错误,避免时间浪费(省时)
  2. 减少多人协作项目的成本,大型项目友好(省心)
  3. 良好代码提示,不用反复文件跳转或者翻文档(省力)

坏处:

  1. 与其它框架结合会有坑
  2. 配置学习成本高
  3. 要学习额外的类型系统

TypeScript 开发环境设置

# 全局安装(可以使用 `tsc` 命令)
npm install -g typescirpt

# 初始化目录
npm init

# 初始化ts环境(会多一个 `tsconfig.json` 文件,即项目的ts配置文件)
tsc --init

关于 tsconfig.json 更多说明,可以参考自动生成的 tsconfig.json 注释或官网文档1

package.json 加上两个 script:

  • build 编译
  • build:w 监听并编译
"scripts": {
"build": "tsc",
"build:w": "tsc -w"
}

基础类型

原始类型

  1. boolean
  2. number
  3. string
  4. void
  5. null & undefined
  6. symbol
  7. bigint

其它常见类型

  1. any
  2. unknown
  3. never
  4. Array
  5. Tuple
  6. Object

boolean 布尔值

const isSpinning: boolean = false;

number 数字

const size: number = 6;

注:除了十进制,还可以是十六进制、二进制、八进制字面量。

string 字符串

const customer: string = 'Tonny';

注:除了单双引字符串,还可以是模板字符串(被反引号包围)。

void 空值

  1. void 表示没有任何类型
  2. 当一个函数没有返回值时,它的返回类型就是 void
  3. 声明一个 void 类型的变量没什么用,因为它只能被赋予 undefinednull
function sayHi(): void {
alert('Hi');
}

null & undefined

  1. 默认情况下,nullundefined 是所有类型的子类型,即可以把它们赋值给以上类型
  2. undefined 能赋值给 void
  3. 只有 --strictNullChecks 设置为 falsenull 才能赋值给 void
let u: undefined = undefined;
let n: null = null;

let v1: void = undefined; // OK
// let v2: void = null; // 注意:strictNullChecks=false时才可以这样赋值

tsconfig.json 开启 strictNullChecks 检查:

{
"compileOnSave": false,
"compilerOptions": {
"strictNullChecks": true,
"skipLibCheck": true,
}
}

symbol 唯一标识

使用 Symbol 时,必须添加 es6 的编译辅助库:

{
"lib": ["ES6", "DOM"],
}

该类型通过 Symbol 构造函数创建:

const s1 = Symbol('s1');
const s2 = Symbol('s2');

Symbol 的值时唯一不变的:

Symbol('s1') = Symbol('s1');    // false

BigInt 大数整数

使用 BigInt 必须添加 ESNext 编译辅助库:

"lib": ["es6", "dom", "ESNext"],

Any 任何值

  1. 可以为还不清楚类型的变量指定该类型 any
  2. 多人协作协作项目大忌,很可能把 TypeScript 变成 AnyScript,所以非不得情况不建议使用
let a: any;

unknown 未知值

  1. ts 3.0 引入的新类型,any 类型对应的安全类型
  2. any 对比,unknown 会更严格:当 unknown 类型被确定是某个类型之前,它不能被进行任何操作,如实例化、getter、函数执行等
  3. 缩小其类型范围可以执行 unknown,如用 instanceof 判断是否日期、数组等具体类型
let a: any;
a[0] = 1; // OK

let un: unknown;
// un[0] = 1; // Error:未确定类型之前不能进行操作
if (un instanceof Array) { // 缩小范围类型,让unknow可以执行操作
un[0] = 1; // OK
}

never 永不存在

  1. never 表示那些永不存在的值的类型
  2. 它是任何类型的子类型,即可以赋值给任何类型
  3. 没有类型是 never 的子类型,即没有任何类型(包括 any)可以赋值给 never(除了 never 本身)
  4. 常见的 never 类型有:总是会抛出异常或根本就不会有返回值的函数表达式;箭头函数表达式的返回值类型;被永不会为真的类型保护所约束的变量
// 返回never的函数必须存在无法到达的终点
function error(message: string): never {
throw new Error(message);
}

Array 数组

两种方式可以定义数组:

// 使用数组泛型,`Array<元素类型>`
let list: Array<number> = [1, 2, 3];

// 使用 `元素类型[]`(这种更常用)
let list: number[] = [1, 2, 3];

Tuple 元组

  1. 与数组类型很相似,表示一个已知元素数量和类型的数组
  2. 元组中元素的类型可以不同
let t: [string, number]
t = ['hi', 10]; // OK
t = [10, 'hi']; // Error: 元素类型顺序有误
t = ['hi', 10, 10]; // Error: 数组长度

Object 对象

  1. Object 表示非原始类型,也就是除了 number, string, boolean, symbol, bigint, nullundefined 之外的类型
  2. 普通对象、枚举、数组、元组都是 object 类型

类型断言与类型守卫

类型断言

  1. 假定你比 TS 更了解某个值的详细信息,这时就可以通过类型断言的方式告诉编译器
  2. 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构
  3. 类型断言有两种形式:“尖括号”语法和 as 语法

“尖括号”语法

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

as 语法

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

一个是使用场景:

// 情况1
// const person = {};
// person.name = 'tommoy'; // Error: 属性不存在
// person.age = 20; // Error: 属性不存在

// 情况2
interface Person {
name: string,
age: number
}
const person = {} as Person;
person.name = 'tommy'; // OK
person.age = 18; // OK

类型守卫

  1. 类型守卫,即缩小类型的范围
  2. 常见使用方式有:instanceofin、字面量类型守卫

instanceof 类型保护是通过构造函数来细化类型的一种方式:

class Cat {
name = 'cici';
color = 'white';
}

class Dog {
name = 'bubu';
weight = 90;
}

function getSometing(arg: Cat | Dog) {
// 类型细化为 `Cat`
if (arg instanceof Cat) {
console.log(arg.color); // OK
// console.log(arg.weight); // Error:类型“Cat”上不存在属性“weight”
}

// 类型细化为 `Dog`
if (arg instanceof Dog) {
// console.log(arg.color); // Error:类型“Dog”上不存在属性“color”
console.log(arg.weight); // OK
}
}

ininstanceof 类似,x in y 表示 x 属性在 y 中存在:

class Cat {
name = 'cici';
color = 'white';
}

class Dog {
name = 'bubu';
weight = 90;
}

function getSometing(arg: Cat | Dog) {
if ('color' in arg) {
console.log(arg.color); // OK
// console.log(arg.weight); // Error:类型“Cat”上不存在属性“weight”
}

if ('weight' in arg) {
// console.log(arg.color); // Error:类型“Dog”上不存在属性“color”
console.log(arg.weight); // OK
}
}

字面量类型守卫:

type Cat = {
name: 'cici';
age: number;
}

type Dog = {
name: 'bubu';
weight: number;
}

function doSomething(arg: Cat | Dog) {
if (arg.name === 'cici') {
console.log(arg.age); // OK
// console.log(arg.weight); // Error: 类型“Cat”上不存在属性“weight”。
} else {
// console.log(arg.age); // Error: 类型“Dog”上不存在属性“age”。
console.log(arg.weight); // OK
}
}

参考

Footnotes

  1. TypeScript: Documentation - What is a tsconfig.json