TS的相关知识

项目中常见的 ts 写法

任意字符串的属性名 [propName: string]

interface SquareConfig {
	name: string;
	[propName: string]: any;
}
let c: SquareConfig = {name: '自定', age: 24, gender: '男'};
type Person = [number, ...string[]]; 
// 一个数组,第一个元素是number,剩下的元素是不确定长度的string
const person: Person = [1, 'str1', 'str2'];
interface Iprop {
	func1: () => string; // 函数返回一个string;
	func2: (a: string, b: number) => void; // 函数输入两个参数,没有返回值;
	str1: string; // str1参数必须是字符串;
	value: Array<{ type: string; money: string; }>; 
	value: { type: string; money: string; }[]; 
	// 数组,每个元素都是一个对象,对象中有两个元素:
	// 一个是type: string类型,一个是money: string类型;
	onChange: (data: Array<{ type: string; money: string; }>) => void;
	// 函数:输入的是数组,没有返回值
	
	unit?: any[]; // 可有可无,如果有,则应该是数组;
}

function greeter(props: Iprop) {
	return ...;
}
class Point {
    x: number;
    y: number;
}
interface Point3d extends Point {
    z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
type Iprop = {
	name: string,
	age: number
}
const obj: Iprop = {
	name: 'Jayson',
	age: 23
}
function of<Type>(a: Type) : Type[] {
  return [a];
}

const toNumbers = of(1); // const toNumbers: number[]
const toStrings = of("Test Of"); // const toString: string[]
const toNumbers = of<number>(1); // const toNumbers: number[]
const toStrings = of<string>("Test Of"); // const toString: string[]
type Condition = {[key:string]: string};
interface ConditionTypes {
  conditionType?: string | any;
  conditions?: Condition | Condition[];
  [propName: string]: any;
}
interface Props {
  conditionTypes: ConditionTypes[];
  onSubmit: (data: any) => void;
  screenHistory: any;
  Layout: any;
}

TS 的核心原则之一:对值所具有的结构进行类型检查;

一、为什么TypeScript横空出世?

TS的出现是为了解决JS存在的问题。那么JS有哪些问题呢?

  • 对于变量来说,没有类型限制,JS属于弱语言类型(动态类型);
let a = 10;
// 之后a可以随意变化类型

目前,变量a属于 number 类型,它想要在其他地方使用,参与运算;
如果一不小心,开发人员将 a 改成了 string 类型或者 Array 类型,那就会出现bug了,但是JS是不报错的(非常头疼)。
在这里插入图片描述

JS起源: JS产生于第一次浏览器大战,大战双方是网景公司和微软公司,网景公司为了和微软公司去拼,设计了JS,时间比较仓促,所以有些设计考虑的不是很周到,比如说IE浏览器的兼容问题;

JS目前在前端属于一个无可代替的语言,它的灵活性和易读性都不可替代;微软公司为了解决JS 动态类型的问题,另辟蹊径,提出了TS的概念;(编辑器VSCode 和 TS 均由微软设计)

二、TypeScript是什么?

TS完全支持JS,可以在任何支持JS的平台中执行;并在JS的基础上增加了一些功能:给变量赋予类型,让JS从一个动态类型的语言变成了静态类型的语言;

但是注意: TS不能被JS解析器直接执行;比如:一个 .ts 文件不能直接在浏览器中执行,需要做一步转化,先将 TS 编译为 JS;(这时候需要安装一个TS编译器)

那么TypeScript增加了什么?

  • 强大的开发工具(一些提示)
  • 丰富的配置选项 (可以将文件转化为任意版本的JS,比如:ES6、ES5等等)
  • 添加ES不具备的新特性

三、TypeScript开发环境搭建

首先需要装一个TS解析器;装TS解析器需要先下载Node.js

  • 下载并安装node.js
  • 使用npm安装TypeScript
    npm i -g typescript
  • 创建一个ts文件
  • 在ts文件目录下使用tsc对ts文件进行编译,编译好之后,会生成一个.js文件
    tsc helloTS.ts

四、TypeScript的类型申明

场景一:基本数据类型

JS:

let a;
a = 10;
a = 'hello';

TS:(TS很友好,发生类型错误时,仍可以编译,但会报错提示开发人员type Error)

// 声明变量,并指定变量类型
let a: number; // a的类型设置为了number,以后的使用过程中,a的值只能是数字
a = 10; // no problem
a = 33; // no problem
a = 'hello'; // 编译器,会标红;
// 并提示 Type 'hello' is not assignable to type 'number'.
// 声明完变量直接进行赋值
let b: boolean = true;
b = 123; // 报错

// 如果变量的声明和赋值同时进行的;TS可以自动对变量进行类型检测
let c = true;
c = 123; // 报错
场景二:函数应用

JS中的函数是不考虑参数的类型个数的;

JS:

function sum(a, b) {
	return a + b;
}
console.log(sum(123, 456); // 579
console.log(sum(123, '456'); // '123456'

TS:

// 函数只有两个参数,参数类型都是number,函数返回值也是number类型
function sum(a: number, b: number): number {
	return a + b;
	// return a + 'hello'; // 会报错
}
let result = sum(123, 456);
TS中的类型:

在这里插入图片描述

  • 联合类型
let b: boolean | string; // 变量b的类型只能是boolean或者string;
let c: {name: string} & {age: number};
c = {name: '孙悟空', age: '18'};
  • 字面量类型
let contract = 'Actor'; //变量contract只能是'Actor'这个string值;
// 字面量类型中的联合类型
let a: 'male' | 'female'; // 变量a的值只能是'male'或者'female',类型是string
  • any 类型
let c: any;
// 一个类型设置为any,相当于对该变量关闭了TS的类型检测
c = 10;
c = 'hello';

使用 any 进行类型限制,相当于移除了类型检查;我们不推荐使用 any ,但它有存在的必要性:比如:

  1. 为编程阶段还不清楚类型的变量指定一个类型,这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查;
  2. 对现有代码进行改写时;
let notSure: any = 4;
notSure.ifItExists(); 
// okay, ifItExists might exist at runtime
notSure.toFixed(); 
// okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); 
// Error: Property 'toFixed' doesn't exist on type 'Object'.

由于在TypeScript语言中,一切皆对象,因此,大部分人会有疑问? Object 会和 any 有相似的作用吗?直接挑明,有;但是,Object类型的变量只允许给它赋任意值,但不能够调用变量上的方法。

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
  • unknown 类型
// unknown 表示未知类型的值
let d: unknown;
d = 10;
d = 'hello';

乍一看,unknown和any类型限制是一样的,实则不然,注意往下看

let e: boolean;
// d 的类型是any,它可以赋值给任意类型
e = c; // any类型祸害了变量e, 现在变量e的值为:'hello'

unknown 类型的变量不能直接赋值给其他变量;

let s: string;
s = d; // 虽然d现在是string类型,但是会标红;
// Type 'unknown' is not assignable to type 'string';
// 因为现在把未知类型的变量赋值给了已知类型的变量s

解决办法:判断类型后再做赋值; // 类型断言

if(typeof e === 'string) {
	s = e;
}

// 类型断言: 告诉解析器,变量的实际类型
s = e as string;
s = <string>e;

在这里插入图片描述
在这里插入图片描述
综上所述,你明白了吗? 尽量不要再用any了

  • void类型:用于函数返回值的限制
function fn(n: number): string | number {
  if(n > 1) {
    return '大于1';
  } else {
    return n;
  }
}

当函数没有返回值的话,使用void;返回null或者返回undefined,都认为是没有返回值。

function fn(n: number): void {
  if(n >= 0 && n < 1) {
    return null;
  } else if(n < 0) {
    return undefined;
  } else {
    console.log(n);
  }
}
  • never类型:表示永远不会返回结果,应用在抛出错误的场景;函数不能返回undefined和null;
function fn(n: number): never {
  if(n >= 0 && n < 1) {
    throw new Error('报错了');
  } else {
    console.log('n 出问题了');
    throw new Error('n 有问题');
  }
}
  • Object类型
let a: object; // 此时a可以是对象,也可以是函数,因为函数也属于对象

// {} 用来指定对象中可以包含哪些属性
// 语法: {属性名: 属性值的类型, 属性名: 属性值的类型};
// 这里注意:属性名后面加一个?,表示属性是可选的
let b: {name: string, age?: number};
b = {name: 'Jayson柴'};

// 任意字符串的属性名 [propName: string]
let c: {name: string, [propName: string]: any};
c = {name: '自定', age: 24, gender: '男'};
  • 函数类型
let a: Function;

// (形参: 类型, 形参: 类型 ...) => 返回值的类型
let b: (a: number, b: number) => number;
b = function(n1: number, n2: number): number {
	return 10;
};
  • 数组类型
// string[] 表示字符串数组
let c: string[];
c = ['a', 'b', 'c'];

// Array<string> 表示字符串数组
let d: Array<string>;
d = ['a', 'b', 'c'];
  • tuple 元祖类型
// 元祖就是固定长度的数组
let a: [string, number?]; // 长度为2的元祖
a = ['hello', 123];
a = ['hello'];
  • enum 枚举类型(TS新增类型)
enum Gender {
	Male = 1,
	Female = 0,
};
let b: {name: string, gender: Gender};
b = {
	name: '柴勇',
	gender: Gender.Male, 
};

默认从0开始为元素编号;

enum Color {Red, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); // Blue
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); // Green
  • 类型断言
    使用场景:明确的知道一个实体具有比它现在类型更确切的类型;TS会假设开发人员已经进行了必须的检查。

类型断言有两种形式:

  1. 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
  1. as语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

⚠️ 当在TypeScript中使用 JSX 时,只有 as 语法断言是被允许 ✅ 的。

  • 定义类型
// 类型别名
type MyType = 1 | 2 | 3 | 4 | 5;
let k: myType;
k = 2;

// 描述一个对象的类型
type Iprop = {
	name: string,
	age: number
}
const obj: Iprop = {
	name: 'Jayson',
	age: 23
}

五、TypeScript编译选项

之前对.ts文件的编译需要输入tsc xx.ts;每当文件内容发生变化,都需要重新输入tsc xx.ts;为了解决这个问题,第一次可以这样输入:

tsc xx.ts -w // -w 表示watch监视,ts文件改变后,会自动监视到文件变化,重新编译;

但是这样会有一个问题:有n个文件,就需要输入n次,比较繁琐;

解决办法:在项目文件夹中添加一个tsconfig.json文件,进行相关配置;

{
  /*
    tsconfig.json是ts编译器的配置文件,
    ts编译器可以根据它的信息来对代码进行编译
      "include" 用来指定哪些ts文件需要被编译
        路径: ** 表示任意目录
              * 表示任意文件
      "exclude": 不需要被编译的文件目录
              默认值:["node_modules", "bower_components", "jspm_packages"]
      "extends": 继承配置文件
      "files": 指定被编译文件的列表,只有需要编译的文件少时才会用到;和include很像
  */
  "include": [
    "./src/**/*",
    "./page/**/*"
  ],
  "exclude": [
    "./src/hello/**/*"
  ],
  "extends": "./config/base",
  "files": [
    "core.ts",
    "sys.ts",
  ],
    /*
    compilerOptions 编译器的选项
  */
  "compilerOptions": {
    "target": "ES6", // 指定ts被编译的ES版本,默认编译为ES3
    "module": "amd", // 指定要使用的模块化的规范,可以选择commonjs, amd, umd等等;
    // es6: import { Hello } from './Hello'; 
    // commonjs: 
    //  const Hello_js_1 = require("Hello.js");
    //  console.log(Hello_js_1.Hello)
    "lib": ["DOM", "ES2015"], // lib 用来指定项目中要使用的库
    "outDir": "./dist", // outDir 用来指定编译后文件所在的目录
    "outFile": "./dist/app.js", // 将代码合并为一个文件
    // 将所有全局作用域的代码编译好之后合并到app.js文件中
    // 使用了模块化,则无法合并
    "allowJs": false, // 是否对js进行编译,默认是false,不编译js文件
    "checkJs": false, // 检查js代码是否符合语法规范。默认是false,不检查
    "removeComments": false, // 编译结果中是否移除注释,默认是false,不移除注释
    "noEmit": false, // 不生成编译后的文件,默认是false,编译后生成文件
    // 改为true后,执行了编译过程,但是不生成编译文件
    "noEmitOnError": false, 
    // 当有错误时,编译后不生成编译后的文件;默认false,不开启功能
    "alwaysStrict": true, // 用来设置编译后的文件是否使用严格模式,默认false,不设置严格模式
    // true:编译后的js文件开头:'use strict'
    // 当代码中有引入 / 导出模块时,默认启用严格模式,不需要在开头声明'use strict'
    "noImplicitAny": false, // 当变量没有设置类型时,默认是any,编译器不会标红
    // true:不允许隐式any,浏览器会标红
    "noImplicitThis": false, // 是否允许this的类型不明确,默认false,允许
    // true:不允许不明确类型的this,一定要知道this的类型是什么;
    "strictNullChecks": false, // 是否严格检查空值;默认false,不严格
    // 下面例子,如果box1没有获取到节点,则box1为一个null
    /*
     let box1 = doucment.getElementById('box1');
     box1.addEventListener('click', function() {
      alert('hello');
     });
    */
    // 解决办法:
    /*
      box1?.addEventListener('click', function() {
        alert('hello');
      });

      if(box1 !== null) {
        box1.addEventListener('click', function() {
          alert('hello');
        }); 
      }
    */
    "strict": false, // 所有严格检查的总开关
    // 如果设为true, 则所有的严格检查都可以不写,比如noImplicitAny等等;
}

六、面向对象

面向对象是程序中一个非常重要的思想;程序之中所有的操作都是通过对象完成的。

举例:

  • 操作浏览器要使用window对象;
  • 操作网页要使用document对象;
  • 操作控制台要使用console对象;
  1. 类中的成员默认被指定为:public, 指定为 public 的成员可以在任何地方使用;
  2. 一旦被指定为 private,则该成员只能在类内部使用,不能在类的实例中使用,也不能在类的子类中使用;
  3. 一旦被指定为 protected ,则该成员可以在类或者子类内部使用,不能在类或者子类的实例中使用。
1、类

创建对象,必须先定义类(可以理解为对象的模型),程序中可以根据类创建指定类型的对象;

class Person {
	name: string;
	age: number;
	constructor(name: string, age: number) {
		// 构造函数会在对象创建时调用
		// 这里的this表示当前的实例对象
		this.属性名 = 参数;
		this.name = name;
		this.age = age;
	}
	
	// 实例方法
	sayHello() {
		console.log('Hello,' + this.name);
	};
	
	// 类方法 / 静态方法,可以通过类去调用,不能通过实例去调用
	static sayHi() {
		console.log('Hi);
	};
}

const per = new Person('霄强', 29);
per.sayHello(); // 调用实例方法  Hello, 霄强
Person.sayHi(); // 调用类方法
2、类的继承
class Men extends People {
	gender: string;
	constructor(name: string, age: number, gender: string) {
		// 如果在子类中写了构造函数,则在子类构造函数中必须通过super调用父类的构造函数
		// 如果子类不写构造函数(constructor),则默认继承父类所有属性和方法
		super(name, age); // 调用父类的构造函数
		this.gender = gender;
	}
	sayHello() {
		// 在类的方法中,super表示当前类的父类
		super.sayHello();
	}
}
3、抽象类

抽象类中可以设置抽象方法,子类必须对抽象方法进行重写;
抽象方法的目的: 防止子类忘记写某个方法;(不重写方法,会标红)

// 以abstract开头的类是抽象类
// 抽象类和其他类区别不大,只是不能用来创建对象
// 抽象类是专门用来被继承的类
// 抽象类中可以添加抽象方法
abstract class Person {
	constructor() {}
	// 定义一个抽象方法
	// 抽象方法使用abstract开头,没有方法体
	// 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
	abstract sayHello(): void;
}
4、接口
// 描述一个对象的类型
type Iprop = {
	name: string,
	age: number
}
const obj: Iprop = {
	name: 'Jayson',
	age: 23
}

接口 interface 用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
同时,接口也可以当作类型声明去使用,此时的用法和 type 一样。

⚠️ 注意

使用 interface 当作类型声明,命名可以一样,应用该类型声明的变量结合多个类型声明使用;
但是type 当作类型声明,命名不能重复;

interface myInterface {
	name: string;
	age: number;
}

interface myInterface {
	gender: string;
}
const obj: myInterface = { name: 'Jayson', age: 23, gender: 'male' };

一个接口可以继承多个接口,创建出多个接口的合成接口;

interface Shape {
    color: string;
}
interface PenStroke {
    penWidth: number;
}
interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

接口可以在定义类的时候去限制类的结构 (和抽象类很相似)但是 接口中所有的属性不能有实际的值;

接口只定义对象的结构,而不考虑实际值;在接口中所有的方法都是抽象方法;

interface myInter {
	name: string;
	sayHello(): void;
}

// 定义类时,可以使类去实现一个接口,实现接口就是使类满足接口的要求
class MyClass implements myInter {
	name: string;
	
	constructor(name: string) {
		this.name = name;
	}
	
	sayHello() {
		console.log('Hello');
	}
}
5、把类当接口使用
class Point {
    x: number;
    y: number;
}
interface Point3d extends Point {
    z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
6、属性的封装(对于一些容易写错的属性可以使用)
class Person {
	name: string;
	age: number;
	constructor(name: string, age: number) {
		this.name = name;
		this.age = age;
	};
}

const per = new Person('霄强', 29); 
console.log(per);  // Person {name: '霄强'. age: 29}
per.name = '柴';
per.age = -29;
console.log(per);  // Person {name: '柴'. age: -29}

上面的写法有一个很大的问题,属性在对象中设置,属性可以被任意的修改,可能会导致对象中的数据变得非常不安全。(比如不小心将age设置为负,或者将money设置为负,可能会出问题)

TS中可以在属性前添加属性的修饰符

  • public 修饰的属性可以在任意位置访问(修改),属性默认是public;
  • private 私有属性,只能在类内部进行访问(无法修改);
  • protected 受保护的属性,只能在当前类和当前类的子类中访问;

⚠️ constructor 也可以使用修饰符 protected

class Person {
    protected name: string;
    protected constructor(theName: string) { this.name = theName; }
}

class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.

⚠️ 但是我们可以通过在类中 添加方法 使得私有属性可以被外部访问

class Person {
	private _name: string;
	private _age: number;
	constructor(name: string, age: number) {
		this._name = name;
		this._age = age;
	};
	getName() {
		return this._name;
	};
	setName(value: string) {
		// 条件设置,防止出错
		if(value >= 0) {
			this._name = value;
		}
	}
}

const per = new Person('霄强', 29); 

console.log(per);  // Person {_name: '霄强'. _age: 29}
console.log(per.getName()); // '霄强'
per.setName('柴');
console.log(per.getName());  // '柴'
console.log(per._name); // 标红,无法访问
per._name = '柴'; // 标红,无法更改属性值

上面的操作可能略显繁琐,我们可以设置 get 和 set 方法

class Person {
	private _name: string;
	private _age: number;
	constructor(name: string, age: number) {
		this._name = name;
		this._age = age;
	};
	get name() {
		return this._name;
	};
	set name(value: string) {
		this._name = value;
	};
}

const per = new Person('霄强', 29); 
console.log(per.name);  // 29
per.name = '柴'; // 可以直接修改name
7、类的简写
class Person {
	name: string;
	age: number;
	constructor(name: string, age: number) {
		this._name = name;
		this._age = age;
	}
}

// 简写: 语法糖
class Person {
	constructor( public name: string, public age: number) {}
}

七、泛型

在定义函数或者类时,遇到类型不明确就可以使用泛型。

// 函数的返回值和函数参数的类型是一致的;
// 不要使用any,使用any就相当于关闭了类型检查;
function fn<T>(a: T): T {
	return a;
}
// 调用具有泛型的函数
let result = fn(10); // 不指定泛型,TS可以自动推断类型 T 为 number;
let result1 = fn<string>('hello'); // 指定泛型;

泛型可以指定多个

function fn2<T, K>(a: T, b: K): T {
	return a;
}
fn2<number, string>(123, 'hello');

对泛型进行限制

interface Inter {
	length: number;
}

function fn3<T extends Inter>(a: T): number {
	return a.length;
}
fn3('hello') // 这里fn3的参数只能是有length的类型
class MyClass<T> {
	name: T;
	constructor(name: T) {
		this.name = name;
	}
}
const myclass = new MyClass<string>('柴勇');

八、TS中的 keyof 的应用

keyof 操作符提取属性名称,常用场景:对象中,获取键对应的值时(只有正确的key才能拿到值,不正确的键会报错);

keyof应用
interface StringIndexArray {
  [index: string]: string;
}

interface NumberIndexArray {
  [index: number]: string;
}

type K1 = keyof StringIndexArray // type K1 = string | number
type K2 = keyof NumberIndexArray // type K2 = number
interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

这里要注意:

function prop(obj: object, key: string) {
  return obj[key];
}

这样使用的话,编辑器会提示错误:

// Element implicitly has an 'any' type because expression of type 'string'
// can't be used to index type '{}'.
元素隐式地拥有 `any` 类型, `string` 类型不能用于索引`{}`类型;

暴力解决方案:

function prop(obj: object, key: string) {
  return (obj as any)[key];
}

更好的解决方案:

定义T类型并使用 extends 关键字约束类型必须是object 类型的子类型 (说白了,传入的obj是个对象就行),然后使用 keyof 操作符获取T类型的所有键,返回类型是联合类型;

下面的案例中,T类型是object类型,K类型继承T类型的所有键;所以K的值必须是id、text、done中的一个;

这样就防止开发人员,读取不存在的类型;比如:prop(todo, "date"); 是会报错的。

type Todo = {
  id: number;
  text: string;
  done: boolean;
}

const todo: Todo = {
  id: 1,
  text: "Learn TypeScript keyof",
  done: false
}

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean
typeof 和 keyof 的关系

typeof 操作符用于获取变量的类型

const COLORS = {
  red: 'red',
  blue: 'blue'
}
// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue'
type Colors = keyof typeof COLORS 
let color: Colors;
const COLORS = {
  red: 'red',
  blue: 'blue'
}

// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue'
type Colors = keyof typeof COLORS 
let color: Colors;
color = 'red' // Ok
color = 'blue' // Ok
// Type '"yellow"' is not assignable to type '"red" | "blue"'.
color = 'yellow' // Error

九、只读属性

interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!

想要再次被修改,只能做类型断言

a = ro as number[];

那么,问题来了?
const 也可以阻止变量修改,那么 constreadonly 有什么区别呢?
什么时候该用 const, 什么时候该用 readonly

看要把它作为变量使用还是作为一个属性:

  • 作为变量使用的话,用 const
  • 若做为属性,则使用 readonly
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值