TypeScript学习(1)
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类型
unknown
跟any
的相似之处,在于所有类型的值都可以分配给unknown
类型。
let x:unknown;
x = true; // 正确
x = 42; // 正确
x = 'Hello World'; // 正确
unknown
类型跟any
类型的不同之处在于,它不能直接使用。主要有以下几个限制
- 首先,
unknown
类型的变量,不能直接赋值给其他类型的变量(除了any
类型和unknown
类型) - 不能直接调用
unknown
类型变量的方法和属性 unknown
类型变量能够进行的运算是有限的,只能进行比较运算(运算符==
、===
、!=
、!==
、||
、&&
、?
)、取反运算(运算符!
)、typeof
运算符和instanceof
运算符这几种,其他运算都会报错。
never 类型
不存在任何属于“空类型”的值,该类型被称为never
,即不可能有这样的值。
let x:never;
变量x
的类型是never
,就不可能赋给它任何值,否则都会报错。
八大基础类型
- boolean
- string
- number
- bigint
- symbol
- object
- undefined
- null
boolean类型
boolean
类型只包含true
和false
两个布尔值。
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';
变量x
是const
命令声明的,TypeScript 就会推断它的类型是值https
,而不是string
类型。
联合类型
联合类型(union types)指的是多个类型组成的一个新类型,使用符号|
表示。
联合类型A|B
表示,任何一个类型只要属于A
或B
,就属于联合类型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]) {
// ...
}