类型大全
类型大全
基本类型
JavaScript 语言(注意,不是 typescript :no-line-numbers)将值分成8种类型。
- boolean
- string
- number
- bigint
- symbol
- object
- undefined
- null
typescript :no-line-numbers 继承了 JavaScript 的类型设计,以上8种类型可以看作 typescript :no-line-numbers 的基本类型。
包装对象类型
JavaScript 的8种类型之中,undefined和null其实是两个特殊值,object属于复合类型,剩下的五种属于原始类型(primitive value),代表最基本的、不可再分的值。
- boolean
- string
- number
- bigint
- symbol
上面这五种原始类型的值,都有对应的包装对象(wrapper object)。所谓“包装对象”,指的是这些值在需要时,会自动产生的对象。
'hello'.charAt(1) // 'e'字符串hello执行了charAt()方法。但是,在 JavaScript 语言中,只有对象才有方法,原始类型的值本身没有方法。这行代码之所以可以运行,就是因为在调用方法时,字符串会自动转为包装对象,charAt()方法其实是定义在包装对象上。
五种包装对象之中,symbol 类型和 bigint 类型无法直接获取它们的包装对象(即Symbol()和BigInt()不能作为构造函数使用),但是剩下三种可以。
- Boolean()
- String()
- Number()
包装对象类型与字面量类型
由于包装对象的存在,导致每一个原始类型的值都有包装对象和字面量两种情况。
'hello' // 字面量new String('hello') // 包装对象上面示例中,第一行是字面量,第二行是包装对象,它们都是字符串。 为了区分这两种情况,typescript :no-line-numbers 对五种原始类型分别提供了大写和小写两种类型。
- Boolean 和 boolean
- String 和 string
- Number 和 number
- BigInt 和 bigint
- Symbol 和 symbol
const s1: String = 'hello'; // 正确
const s2: String = new String('hello'); // 正确
const s3: string = 'hello'; // 正确
const s4: string = new String('hello'); // 报错Symbol()和BigInt()这两个函数不能当作构造函数使用,所以没有办法直接获得 symbol 类型和 bigint 类型的包装对象,除非使用下面的写法。但是,它们没有使用场景,因此Symbol和BigInt这两个类型虽然存在,但是完全没有使用的理由。
提示
没有使用意义
let a = Object(Symbol());
let b = Object(BigInt());Object 类型与 object 类型
typescript :no-line-numbers 的对象类型也有大写Object和小写object两种。
Object 类型
大写的Object类型代表 JavaScript 语言里面的广义对象。所有可以转成对象的值,都是Object类型,这囊括了几乎所有的值。
let obj: Object;
obj = true;
obj = 'hi';
obj = 1;
obj = {foo: 123};
obj = [1, 2];
obj = (a: number) => a + 1;事实上,除了undefined和null这两个值不能转为对象,其他任何值都可以赋值给Object类型。
let obj: Object;
obj = undefined; // 报错
obj = null; // 报错空对象{}是Object类型的简写形式,所以使用Object时常常用空对象代替。
object 类型
小写的object类型代表 JavaScript 里面的狭义对象,即可以用字面量表示的对象,只包含对象、数组和函数,不包括原始类型的值。
let obj: object;
obj = {foo: 123};
obj = [1, 2];
obj = (a: number) => a + 1;
obj = true; // 报错
obj = 'hi'; // 报错
obj = 1; // 报错注意
大多数时候,我们使用对象类型,只希望包含真正的对象,不希望包含原始类型。所以,建议总是使用小写类型object,不使用大写类型Object。
无论是大写的Object类型,还是小写的object类型,都只包含 JavaScript 内置对象原生的属性和方法,用户自定义的属性和方法都不存在于这两个类型之中。
undefined 和 null 的特殊性
undefined和null既是值,又是类型。
let age: number = 24;
age = null; // 正确
age = undefined; // 正确JavaScript 的行为是,变量如果等于undefined就表示还没有赋值,如果等于null就表示值为空。所以,TypeScript 就允许了任何类型的变量都可以赋值为这两个值。
但是有时候,这并不是开发者想要的行为,也不利于发挥类型系统的优势。
const obj: object = undefined;
obj.toString() // 编译不报错,运行就报错提示
为了避免这种情况,及早发现错误,TypeScript 提供了一个编译选项strictNullChecks。只要打开这个选项,undefined和null就不能赋值给其他类型的变量(除了any类型和unknown类型)。
// tsc --strictNullChecks app.ts
let age: number = 24;
age = null; // 报错
age = undefined; // 报错{
"compilerOptions": {
"strictNullChecks": true
}
}// 打开 strictNullChecks
let x: undefined = null; // 报错
let y: null = undefined; // 报错总之,打开strictNullChecks以后,undefined和null只能赋值给自身,或者any类型和unknown类型的变量。
值类型
TypeScript 规定,单个值也是一种类型,称为“值类型”。
let x: 'hello';
x = 'hello'; // 正确
x = 'world'; // 报错TypeScript 推断类型时,遇到const命令声明的变量,如果代码里面没有注明类型,就会推断该变量是值类型。
// x 的类型是 "https"
const x = 'https';
// y 的类型是 string
const y: string = 'https';
// x1 的类型是 { foo: number }
const x1 = {foo: 1};提示
值类型可能会出现一些很奇怪的报错。
const x: 5 = 4 + 1; // 报错上面示例中,等号左侧的类型是数值5,等号右侧4 + 1的类型,TypeScript 推测为number。由于5是number的子类型,number是5的父类型,父类型不能赋值给子类型,所以报错了。
但是,反过来是可以的,子类型可以赋值给父类型。
let x: 5 = 5;
let y: number = 4 + 1;
x = y; // 报错
y = x; // 正确联合类型
联合类型(union types)指的是多个类型组成的一个新类型,使用符号|表示。
联合类型A|B表示,任何一个类型只要属于A或B,就属于联合类型A|B。
let x: string | number;
x = 123; // 正确
x = 'abc'; // 正确
let setting: true | false;
let gender: 'male' | 'female';
let rainbowColor: '赤' | '橙' | '黄' | '绿' | '青' | '蓝' | '紫';提示
打开编译选项strictNullChecks后,其他类型的变量不能赋值为undefined或null。这时,如果某个变量确实可能包含空值,就可以采用联合类型的写法。
let name: string | null;
name = 'John';
name = null;交叉类型
交叉类型(intersection types)指的多个类型组成的一个新类型,使用符号&表示。
交叉类型A&B表示,任何一个类型必须同时属于A和B,才属于交叉类型A&B,即交叉类型同时满足A和B的特征。
let x: number & string;上面示例中,变量x同时是数值和字符串,这当然是不可能的,所以 TypeScript 会认为x的类型实际是never。
let obj:
{ foo: string } &
{ bar: string };
obj = {
foo: 'hello',
bar: 'world'
};type A = { foo: number };
type B = A & { bar: number };type 命令
type Age = number;
let age: Age = 55;别名不允许重名。
type Color = 'red';
type Color = 'blue'; // 报错别名的作用域是块级作用域。这意味着,代码块内部定义的别名,影响不到外部。
type Color = 'red';
if (Math.random() < 0.5) {
type Color = 'blue';
}别名支持使用表达式,也可以在定义一个别名时,使用另一个别名,即别名允许嵌套。
type World = "world";
type Greeting = `hello ${World}`;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"TypeScript 将typeof运算符移植到了类型运算,它的操作数依然是一个值,但是返回的不是字符串,而是该值的 TypeScript 类型。
const a = {x: 0};
type T0 = typeof a; // { x: number }
type T1 = typeof a.x; // number提示
这种用法的typeof返回的是 TypeScript 类型,所以只能用在类型运算之中(即跟类型相关的代码之中),不能用在值运算。
也就是说,同一段代码可能存在两种typeof运算符,一种用在值相关的 JavaScript 代码部分,另一种用在类型相关的 TypeScript 代码部分。
let a = 1;
let b: typeof a;
if (typeof a === 'number') {
b = a;
}type T = typeof Date
(); // 报错上面示例会报错,原因是 typeof 的参数不能是一个值的运算式,而Date()需要运算才知道结果。
typeof命令的参数不能是类型。
type Age = number;
type MyAge = typeof Age; // 报错块级类型声明
TypeScript 支持块级类型声明,即类型可以声明在代码块(用大括号表示)里面,并且只在当前代码块有效。
if (true) {
type T = number;
let v: T = 5;
} else {
type T = string;
let v: T = 'hello';
}