在进入正题前先说下在 TS 中声明的变量默认是全局的。也就是如果你在 1.ts 文件中声明了一个变量 testStr ,在其他 ts 文件中就不能再次声明 testStr 这个变量了。解决的办法是每个 ts 文件内加上 export {}
语句将文件变为模块文件。
在 TS 中 :
前面为变量,后面为给变量指定的类型。冒号前后有没有空格都行。
原始值
JS 中现在有七种原始值:Undefined、Null、Boolean、Number、String、 Symbol、BigInt:
// Block-scoped variable 'undefined' used before its declaration.
// const undefined: undefined = undefined;
// null' is not allowed as a variable declaration name.
// const null: null = null
const a: undefined = undefined;
const b: null = null;
const bool: boolean = true;
const num: number = 10;
const str: string = 'hello world';
// // Symbol表示独一无二
const s1 = Symbol('key');
const s2 = Symbol('key');
// semantic error TS2367: This condition will always return 'false' since the types 'typeof s1' and 'typeof s2' have no overlap.
// console.log(s1 == s2); // false
这里重点说下 null 和 undefined,在 TS 中 null 和 undefined 有它们各自的类型分别是 null 和 undefined。
在非严格模式下 null、undefined 是所有其他类型的子类型,我们可以将 null、undefined 赋值给其他类型。在严格模式下null和undefined的只能赋值给 unknow、any 和它们自身,undefined 还可以赋值给 void。
// 非严格模式下可以赋值
const a: number = null
const b: string = undefined
const c: boolean = null
// 严格模式下只能赋值给unknow、any和它们自身
// 无论是否是严格模式undefined都可以赋值给void类型
const d: void = undefined
const e: unknown = null
const f: null = null
const g: undefined = undefined
const h: any = null
TS 中严格模式可以通过配置文件 tsconfig.json 进行配置
{
"compilerOptions": {
//...
"strict": true, /* Enable all strict */
"strictNullChecks": true, /* Enable strict null checks. */
}
}
任何类型的子类型,如果 strictNullChecks 的值为 true,则不能把 null 和 undefined 付给其他类型
let name:number | boolean;
name = null;
数组
数组类型可以使用下面两种方式编写,下面以创建一个存放数字数组为例:
const arr1:number[] = [1, 2, 3];
const arr2:Array<number> = [1, 2, 3]
上面声明的两个数组只能存放数字,如果我们想放字符串类型该怎么办呢?
let arr3:(number|string)[] = [1,'2',3]; //使用联合类型声明数组
let arr4:Array<number | string> = [1,'2',3]; // 使用泛型方式声明
元组类型
元组和 JS 中的数组类似,元组类型用来表示一个具有固定数量的元素的数组,且这些元素的类型是已知的。
声明一个元组类型:
// 元素类型需要和声明一一对应
let tuple:[string, number, boolean] = ['hello', 10, true];
// semantic error TS2322: Type '[string, number, true, false]' is not assignable to type '[string, number, boolean]'.
Source has 4 element(s) but target allows only 3.
// let tuple: [string, number, boolean] = [ 'hello', 10, true, false ];
给元组中添加元素,只能存放元组声明时指定的类型。
// 不能像下面这样通过索引给元组添加元素
// semantic error TS2322: Type '1' is not assignable to type 'undefined'.
// tuple[3] = 1
touple.push(1)
touple.push('a')
touple.push(true)
// 不能放元组中没有声明的类型
// Argument of type '{}' is not assignable to parameter of type 'string | number | boolean'.
// touple.push({})
console.log(touple) // ['hello', 10, true, 1, 'a', true]
枚举类型
枚举是一种为数值集提供更友好名称的方式
enum Color {
Red,
Green,
Blue,
}
let c: Color = Color.Green;
console.log(c) // 1
// 可以枚举也可以反举
console.log(Color[0]) // 'Red'
编译成JS后的结果
(function () {
'use strict';
var Color;
(function (Color) {
Color[Color["Red"] = 0] = "Red";
Color[Color["Green"] = 1] = "Green";
Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
var c = Color.Green; // 1
console.log(Color[0]); // 'Red'
console.log(c);
}());
从上面的例子可以看出枚举的索引默认从0开始,我们也可以手动设置枚举的值:
enum Color {
Red = 10,
Green = 5,
Blue,
Orange = 'orange',
// Enum member must have initializer, Yellow需要手动赋初始值
Yellow = 1
}
console.log(Color.Red) // 10
console.log(Color[10]) // 'Red'
console.log(Color.Blue) // 6
console.log(Color.Orange) // orange
console.log(Color['orange']) // Element implicitly has an 'any' type because index expression is not of type 'number'
// console.log(Color.Yellow) // '1'
从上面这个例子我们可以得出四个结论:
- 枚举成员的初始值可以手动设置
- 如果枚举成员没有赋初始值,它的上一个成员的初始值是数字,则它代表的值是上一个成员初始值加1,如枚举成员Blue;如果它的上一个成员的初始值是字符串,那么它必须手动赋初始值才行,如枚举成员Yellow。
- 枚举成员的初始值可以是数字或字符串
- 数字枚举可以反举,字符串枚举不行,如枚举成员Orange
常量枚举
Const 枚举是在枚举上使用 Const 修饰符定义的枚举,可以避免在访问枚举值时产生额外的代码和额外的间接开销。const enum Enum {
A = 1
}
console.log(Enum.A)
上面这个枚举使用const修饰后得到的编译结果为
(function () {
'use strict';
console.log(1 /* A */);
}());
不用const修饰时得到的编译结果为
(function () {
'use strict';
var Enum;
(function (Enum) {
Enum[Enum["A"] = 1] = "A";
})(Enum || (Enum = {}));
console.log(Enum.A);
}());
可以看出使用const修饰后编译后产生的代码量更小。Const枚举只能使用常量枚举表达式,而且与常规枚举不同,它们在编译过程中会被完全删除,因为const枚举不能有计算成员,常量枚举不能反举。
外部枚举
外部枚举和非外部枚举之间有一个重要的区别,在常规枚举中,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。
declare enum Enum {
A = 1,
B,
C = 2
}
unknown类型
当我们声明的变量的时候并不知道这个变量会接收什么样的值时可以给变量赋值为 unknown 类型
let a: unknown
a = 1
a = true
a = 'hello'
但是对象不行
let obj: unknown
obj = {
name: 'f'
}
// semantic error TS2571: Object is of type 'unknown'.
console.log(obj.name, obj.age)
// semantic error TS2571: Object is of type 'unknown'.
let arr: unknown = [ 1, 2, 3 ];
arr.push(4);
console.log(arr);
any类型
如果我们在编写代码的时候不希望使用类型检测或者不知道会给这个变量赋值什么类型,这个时候可以将变量声明成 any 类型。
const arr: any = [1, '2', true, {}]
如果变量被声明为其他类型,则在后续的赋值过程中只能赋值给之前指定的类型,不能随意赋值其他类型。但是如果一个变量在刚开始被指定为 any 类型,后续赋值的过程中可以给它赋值任何类型。
let a: number = 1;
// 报错: Type 'string' is not assignable to type 'number'.
a = '2';
// 在后续的赋值过程中,可以给变量 b 赋值任何其他类型
let b: any = 1;
b = '';
b = false;
b = {};
前面我们讲过了 unknown 类型,这里我们拿它和 any 类型做一个对比。与 unknown 不同,any 类型的变量允许访问任意属性,甚至是不存在的属性。这些属性包括函数,TypeScript 不会检查它们是否存在:
const num1: any = 1
num1.print()
num1.toFixed()
// 使用 unknown 则会报错
let num2: unknown = 4;
// Object is of type 'unknown'
num2.toFixed();
如果我们在声明变量的时候没有给它指定类型也没有赋初始值,可以认为将它声明成了 any 类型,在后续的赋值过程中我们可以给它赋值任何类型。
let a;
a = 1;
a = '';
a = false;
any 类型带来便利的同时也失去了类型安全检测,类型安全是使用 TypeScript 的主要动机之一,应该尽量避免使用 any 类型。
any 的使用场景:在某些情况下,并不是所有类型信息都可用,或者它的声明将花费不适当的工作量。这些可能发生在没有TypeScript 或第三方库的代码中。在这些情况下,我们可能希望选择退出类型检查。为此,我们用 any 类型来标记这些值。
void类型
void 类型一般用于函数的返回值,表示这个函数没有返回值。
function warnUser(): void {
console.log("This is my warning message");
}
将一个变量声明成 void 的类型是没有意义的,因为只能给它们赋值 null (非严格模式)或 undefined。
never类型
never 类型是任何类型的子类型,never 代表不会出现的值。除了 never 类型不能把其他类型赋值给 never 类型。never 类型表示永不出现的值的类型。
// Function returning never must not have a reachable end point
function error(message: string): never {
throw new Error(message);
}
// Inferred return type is never
function fail() {
return error("Something failed");
}
// Function returning never must not have a reachable end point
function infiniteLoop(): never {
while (true) {}
}
never 和 void 区别:
在 strictNullChecks
设置为 false 的情况下 void 可以声明为 null 和 undefined;never 不能赋值任何类型;返回类型为 void 的函数还可以执行;返回类型为 never 类型的函数不能再执行了。
// strictNullChecks 为 true,赋值 null 会报错
let a: void = null;
let b: void = undefined;
function fn(): void {
// strictNullChecks 为 true,返回 null 会报错
// semantic error TS2322: Type 'null' is not assignable to type 'void'.
// return null;
return undefined;
}
function fn1(): never {
// semantic error TS2322: Type 'null' is not assignable to type 'never'.
// return null;
// semantic error TS2322: Type 'undefined' is not assignable to type 'never'.
// return undefined;
throw 1;
}
Symbol类型
Symbol 表示独一无二
const s1 = Symbol('key');
const s2 = Symbol('key');
console.log(s1 == s2); // false
BigInt类型
const num1 = Number.MAX_SAFE_INTEGER + 1;
const num2 = Number.MAX_SAFE_INTEGER + 2;
console.log(num1 == num2); // true
// BigInt只能和BigInt相加
const num3 = BigInt(num1) + BigInt(1);
const num4 = BigInt(num2) + BigInt(2);
const num5 = num1 + 1;
const num6 = num2 + 1;
console.log(num5 == num6) // true
const num7 = Infinity + 1;
const num8 = Infinity + 2;
console.log(num3 == num4); // false
console.log(num5 == num6); // true
console.log(Number.MAX_SAFE_INTEGER < Number.MAX_VALUE); // true
console.log(Number.MAX_VALUE < Infinity) // true
Number.MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的安全整数 // todo
BigInt类型只能和BigInt类型进行操作
// semantic error TS2365: Operator '+' cannot be applied to types 'bigint' and '2'.
let num3 = BigInt(Number.MAX_SAFE_INTEGER) + 2;
也可以简写:
console.log(BigInt(1)+2n) // 3n
但如果想让上面的代码正常运行的话,需要改写 tsconfig.json
文件,将 "target"
改为 "ESNext"
:
{
"compilerOptions": {
"target": "ESNext",
}
object对象类型
object表示非原始类型
let create = (obj:object):void=>{}
create({});
create([]);
create(function(){})