TypeScript ---- 初识基础篇

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类型
    TypeScriptJavaScript Object拆成了两个接口来定义:
  1. 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;
}
  1. 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>;
  // ...
}
  1. Object的所有实例都继承了Object接口的所有属性和方法:
function f(x: Object): { toString(): string } {
  return x; // OK
}
  1. object类型也可以访问Object接口上定义的所有属性和方法:
let bar: object = {};
bar.toString(); // "[object Object]"
bar.hasOwnProperty("abc"); // false
  1. 有趣的是,由于JavaScript的装箱拆箱机制,基本类型有能力访问Object.prototype原型对象上的属性。因此,在TS Object类型可以同时接受引用类型和基本类型(不包括undefinednull)。但object类型不能接受原始值。
let b: Object = 1; // OK
let h: object = 1; // ERROR
  • iii. { }
  1. {}描述一个没有成员的对象,试图访问它的任何属性的时候,TS都会抛出错误。该类型也可以访问Object类型上的所有属性和方法:
const obj: {} = {};
console.log(obj.name); // ERROR,没有这个属性
obj.toString(); // "[object, Object]"
  1. {}也可以被赋予一个初始值:
let obj: {};
obj = 3; // OK
  1. 虽然Object{}都可以接受基本类型的值,但不包括nullundefined
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分支里面,我们把收窄为neverstr赋值给一个显示声明的never变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事修改了IsStrNum的类型:

type IsStrNum  = string | number; => type IsStrNum  = string | number | boolean;

然而他忘记同时修改controlFlowAnalysisWithNever方法中的控制流程,这时候else分支的str类型会被收窄为boolean类型,导致无法赋值给never类型,这时就会产生一个编译错误。通过这个方式,我们可以确保controlFlowAnalysisWithNever方法总是穷尽了IsStrNum的所有可能类型。 通过这个示例,我们可以得出一个结论:使用never避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。

至此,关于TypeScript基础篇的基础数据类型就写到这里,看到这里给大家抛出一个疑问:TypeScript总共有多少种数据类型?评论区留下你的答案。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
vue-typescript-import-dts 是一个用于为 Vue.js 项目中的 TypeScript 文件生成类型声明文件的工具。在 Vue.js 项目中使用 TypeScript 进行开发时,我们经常需要为一些第三方库或自定义组件编写类型声明文件,以提供更好的代码提示和类型检查。 使用 vue-typescript-import-dts 工具可以自动分析 TypeScript 文件中的导入语句,并根据导入的模块生成对应的类型声明文件。这样,在使用该模块时,IDE 或编辑器就能提供准确的代码补全和类型检查。 例如,假设我们的项目中使用了一个名为 axios 的第三方库进行网络请求,但是该库并没有提供类型声明文件。我们可以通过 vue-typescript-import-dts 工具,在我们的 TypeScript 文件中导入 axios,并正确配置工具,它将自动为我们生成一个 axios.d.ts 类型声明文件。 具体使用 vue-typescript-import-dts 的步骤如下: 1. 在项目中安装 vue-typescript-import-dts,可以使用 npm 或 yarn 命令来安装。 2. 在 TypeScript 文件中,使用 import 语句导入需要生成类型声明文件的模块。 3. 在项目根目录下创建一个 .vue-typescript-import-dts.json 配置文件,用来配置生成类型声明文件的规则。可以指定生成的声明文件的输出路径、文件名等。 4. 运行 vue-typescript-import-dts 命令,它会自动扫描 TypeScript 文件中的导入语句,并根据配置生成相应的类型声明文件。 这样,在我们编写代码时,IDE 或编辑器就可以准确地为我们提供代码补全和类型检查的功能。这对于提高开发效率和代码质量非常有帮助。 总之,vue-typescript-import-dts 是一个便捷的工具,可以自动为 Vue.js 项目中使用的第三方库或自定义组件生成类型声明文件,提供更好的代码提示和类型检查功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值