TypeScript ---- 初识基础篇
写在前面:本篇文章篇幅有点长,适合正打算学习TS的小白,如果有一定基础的大神可以忽略本篇文章。文章并非完全原创,大部分内容都是从网上其他文章搬运过来的,再在其中添加上自己的一些理解和总结,如果有涉及侵权请联系我,会及时做出整改。
一、基本类型
1.1 String类型
let name: string = "张三";
// ES5: var name = "张三";
1.2 Number类型
let num: number = 3;
// ES5: var num = 3;
1.3 Boolean类型
let bool: boolean = false;
// ES5: var bool = false;
1.4 Symbol类型
const symb = Symbol();
let obj = {
[symb]: "张三"
}
console.log(obj[symb]); // 张三
1.5 Array类型
let list: number[] = [1, 2, 3];
// ES5: var list = [1,2,3];
let list: Array<number> = [1, 2, 3]; // Array<number>属于泛型语法
// ES5: var list = [1,2,3];
1.6 Enum类型
枚举类型。使用枚举可以定义一些带名字的常量。使用枚举可以清晰的表达意图或创建一组有区别的用例。TypeScript支持数字和字符串枚举。
- i. 数字枚举
enum Direction {
UP,
RIGHT,
DOWN,
LEFT
}
let dir: Direction = Direction.UP;
// ES5:
"use strict";
var Direction;
(function(Direction) {
Direction[(Direction["UP"] = 0)] = "UP";
Direction[(Direction["RIGHT"] = 1)] = "RIGHT";
Direction[(Direction["DOWN"] = 2)] = "DOWN";
Direction[(Direction["LEFT"] = 3)] = "LEFT";
})(Direction || (Direction = {}));
var dir = Direction.UP;
默认情况下,UP的初始值为0,其余的成员会从1开始自动增长。换句话说,Direction.RIGHT的值为1,Direction.DOWN的值为2,Direction.LEFT的值为3。
- 也可以给枚举值设置初始值,则枚举值会从1开始自增,即1,2,3,4。
enum Direction {
UP = 1,
RIGHT,
DOWN,
LEFT
}
let dir: Direction = Direction.UP;
// ES5:
"use strict";
var Direction;
(function(Direction) {
Direction[(Direction["UP"] = 1)] = "UP";
Direction[(Direction["RIGHT"] = 2)] = "RIGHT";
Direction[(Direction["DOWN"] = 3)] = "DOWN";
Direction[(Direction["LEFT"] = 4)] = "LEFT";
})(Direction || (Direction = {}));
var dir = Direction.UP;
- 在中间的枚举值设置初始值,
Enum不会关心也不会计算这个值的生成,而是会从0开始自增,遇见设置的初始值时,随后由这个初始值开始递增。
enum Direction {
UP,
RIGHT,
DOWN = 3,
LEFT
}
let dir: Direction = Direction.UP;
// ES5:
"use strict";
var Direction;
(function(Direction) {
Direction[(Direction["UP"] = 0)] = "UP";
Direction[(Direction["RIGHT"] = 1)] = "RIGHT";
Direction[(Direction["DOWN"] = 3)] = "DOWN";
Direction[(Direction["LEFT"] = 4)] = "LEFT";
})(Direction || (Direction = {}));
var dir = Direction.UP;
- 通过函数方式赋值。声明枚举初始值时,可以通过函数返回的值赋值。注意,一旦其中一个枚举值通过函数方式赋值,则剩余所有参数的枚举值不能没有初始化的值,否则就会报
Error。
funciton getVal(): number {
return 1;
}
enum Direction {
Up = getVal(),
Right = getVal(),
Down, // ERROR,这里不能不赋予初始枚举值
Left = getVal(),
}
- ii. 字符串枚举
字符串枚举没有自增长的行为,每个枚举值都必须用字符串字面量进行赋值。
enum Direction {
Up = "UP",
Right = "RIGHT",
Down = "DOWN",
Left = "LEFT"
}
- 如果不对枚举值进行字符串字面量赋值,则枚举值会默认以数值枚举值进行自增长行为。
- 一旦使用字符串枚举,则所有的值都要有初始值,否则会报错。
enum Direction {
Up = "UP",
Right = "RIGHT",
Down = "DOWN",
Left // ERROR,需要有初始值
}
- iii. 异构枚举
Enum支持枚举成员可以是number又可以是string,但不建议这样做。因为实际上这么做没有什么意义,除非万不得已不建议这样操作。建议还是声明同种类型的枚举值。
enum Check {
No = 0,
Yes = "YES"
}
// ES5
(function (Check) {
Check[Check["No"] = 0] = "No";
Check["Yes"] = "YES"
})(Check || (Check = {}))
1.7 Any类型
在Typescript中,任何类型都可以被归为Any类型。这让Any类型成为了类型系统的顶级类型(也被称为全局超级类型)。
let notType: any = 666;
notType = "张三";
notType = false;
Typescript允许我们对它执行任何操作,无需执行事先任何形式的检查:
let value: any;
value.foo.bar;
value.trim();
value();
new value();
value[0][1];
但这其实并不符合Typescript设计的初衷,尤其是无法使用系统提供的大量保护机制,所以一般很少用到。
1.8 Unknown类型
Unknown是系统的另一种顶级类型,所有类型的值都可以赋值给Unknown。
let value: unknown;
value = true;
value = 42;
value = "Hello World";
value = [];
value = {};
value = Math.random;
value = null;
value = undifined;
value = new TypeError();
value = Symbol();
但不可将Unknown的值赋值给其他类型的变量,除了Any类型和他同种类型的变量。
let value: unknown;
let value1: unknown = value;
let value2: any = value;
let value3: boolean = value; // ERROR
let value4: number = value; // ERROR
let value5: string = value; // ERROR
let value6: object = value; // ERROR
let value7: any[] = value; // ERROR
let value8: Function = value; // ERROR
此外,Unknown类型的值不可以进行其他操作。
let value: unknown;
value.foo.bar; // ERROR
value.trim(); // ERROR
value(); // ERROR
new value(); // ERROR
value[0][1]; // ERROR
将value的变量类型设置为Unknown之后,这些操作都不再被认为是类型正确的。通过将Any类型改变为Unknown类型,我们可以将本来允许更改的配置,改为禁止任何更改。
1.8 Tuple类型
元组类型。TypeScript的数组一般由同种类型的值组成,如果要在单个变量中储存不同类型的值,这时候我们可以使用元组。
let tupleType: [string, boolean];
tupleType = ["张三", true];
使用元组时,每一个属性都有一个关联的类型,必须提供每个属性的值,如果出现类型不匹配或没有值,则会报错 。我们可以通过下标的方式访问元组中的元素:
let tupleType: [string, boolean];
tupleType = ["张三"]; // ERROR,第二个值不能为空
tupleType = ["123"]; // ERROR,类型不匹配
console.log(tupleType[0]); // 张三
console.log(tupleType[1]); // true
1.9 Void类型
Void类型与Any类型相反,表示没有任何类型。当一个函数没有返回值时,可以将函数类型设置为该类型:
function warnUser(): void {
console.log("warning!");
}
// ES5
"use strict";
function warnUser() {
console.log("warning!");
}
注意,声明一个void类型的变量没有任何作用,在严格模式下,该变量的值只能为undifined:
let unusable: void = undefined;
1.9 Null和Undifined类型
Null类型:
let n: null = null;
Undifined类型:
let u: undefined = undefined;
注意,两者互不相通,跟JavaScript保持一致。
1.10 object,Object,{}类型
- i. object类型
object类型表示任何非原始(基本类型)值的类型,包括对象、函数和数组等
let a: object;
a = {}; // OK
a = [1, 2, 3]; // OK
a = [1, true, "张三"]; // OK
a = () => 1; // OK
a = 66; // ERROR,不能将类型number分配给类型object
- 对于
JavaScript而言,只有(大)Object,没有(小)object,(小)object只是typeof判断类型返回的一个字符串而已。 - 例如,在
ES6中,WeakMap要求键必须是对象,在TypeScript中定义的WeakMap使用的正是object约束键的类型:
interface WeakMap<K extends object, V> {
delete(key: K): boolean;
get(key: K): V | undefined;
has(key: K): boolean;
set(key: K, value: V): this;
}
- ii. Object类型
TypeScript把JavaScript Object拆成了两个接口来定义:
interface Object接口类型,用于定义JS Object的原型对象Object.prototype:
interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueOf(): Object;
hasOwnProperty(v: PropertyKey): boolean;
isPrototypeOf(v: Object): boolean;
propertyIsEnumerable(v: PropertyKey): boolean;
}
interface ObjectConstructor接口类型用于定义Object的自身属性,如Object.create():
interface ObjectConstructor {
new(value?: any): Object;
(): any;
(value: any): any;
readonly prototype: Object;
getPrototypeOf(o: any): any;
getOwnPropertyNames(o: any): string[];
create(o: object | null): any;
defineProperty<T>(o: T, p: PropertyKey, ...): T;
freeze<T>(a: T[]): readonly T[];
freeze<T extends Function>(f: T): T;
freeze<T>(o: T): Readonly<T>;
// ...
}
Object的所有实例都继承了Object接口的所有属性和方法:
function f(x: Object): { toString(): string } {
return x; // OK
}
object类型也可以访问Object接口上定义的所有属性和方法:
let bar: object = {};
bar.toString(); // "[object Object]"
bar.hasOwnProperty("abc"); // false
- 有趣的是,由于
JavaScript的装箱拆箱机制,基本类型有能力访问Object.prototype原型对象上的属性。因此,在TS Object类型可以同时接受引用类型和基本类型(不包括undefined和null)。但object类型不能接受原始值。
let b: Object = 1; // OK
let h: object = 1; // ERROR
- iii. { }
{}描述一个没有成员的对象,试图访问它的任何属性的时候,TS都会抛出错误。该类型也可以访问Object类型上的所有属性和方法:
const obj: {} = {};
console.log(obj.name); // ERROR,没有这个属性
obj.toString(); // "[object, Object]"
{}也可以被赋予一个初始值:
let obj: {};
obj = 3; // OK
- 虽然
Object和{}都可以接受基本类型的值,但不包括null和undefined:
let obj: {} = null || undefined; // ERROR
let obj: Object = null || undefined; // ERROR
总结,从严谨程度上说,object > Object > {}。前两者不能赋值给{}。Object是宽泛通用的,{}是宽泛,但并不完全通用的类型。object是最严谨的类型,经常用来约束变量。
从权限程度上说,{} > object > Object。前两者(包括其他基本类型)由于拆装箱机制的存在,可以访问到Object的原型属性。object可以访问到除了基本类型的值以外的属性。但{}完全不能访问。
因此,在约束对象类型的时候,应该始终使用object。我们在定义对象类型的时候,应该多使用{}。尽可能少的去使用Object。
1.11 Never类型
Never类型表示那些永不存在的值的类型。例如,Never类型是那些总会抛出异常或根本就不会有返回值的函数表达式的返回值的类型:
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while(true) {}
}
在TS中,我们可以利用never类型的特性来实现全面性检查:
type IsStrNum = string | number;
function controlFlowAnalysisWithNever(str: isStrNum) {
if (typeof str === "string") {
// 收窄为 string 类型
} else if (typeof str === "number") {
// 收窄为 number 类型
} else {
const result: never = str;
}
}
注意在else分支里面,我们把收窄为never的str赋值给一个显示声明的never变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事修改了IsStrNum的类型:
type IsStrNum
= string | number; => type IsStrNum
= string | number | boolean;
然而他忘记同时修改controlFlowAnalysisWithNever方法中的控制流程,这时候else分支的str类型会被收窄为boolean类型,导致无法赋值给never类型,这时就会产生一个编译错误。通过这个方式,我们可以确保controlFlowAnalysisWithNever方法总是穷尽了IsStrNum的所有可能类型。 通过这个示例,我们可以得出一个结论:使用never避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。
至此,关于TypeScript基础篇的基础数据类型就写到这里,看到这里给大家抛出一个疑问:TypeScript总共有多少种数据类型?评论区留下你的答案。
最后
以上就是花痴橘子最近收集整理的关于TypeScript ---- 初识基础篇TypeScript ---- 初识基础篇的全部内容,更多相关TypeScript内容请搜索靠谱客的其他文章。
发表评论 取消回复