这是我参与「第五届青训营 」伴学笔记创造活动的第 6 天

TypeScript 介绍

  1. TypeScript 是 JavaScript 的超集,供给了 JavaScript 的一切功用,并供给了可选的静态类型、Mixin、类、接口和泛型等特性。
  2. TypeScript 的目标是经过其类型体系帮助及早发现过错并进步 JavaScript 开发效率。
  3. 经过 TypeScript 编译器或 Babel 转码器转译为 JavaScript 代码,可运行在任何浏览器,任何操作体系。
  4. 任何现有的 JavaScript 程序都能够运行在 TypeScript 环境中,并只对其间的 TypeScript 代码进行编译。
  5. 在完好保存 JavaScript 运行时行为的基础上,经过引进静态类型界说来进步代码的可维护性,减少或许呈现的 bug。
  6. 永远不会改动 JavaScript 代码的运行时行为,例如数字除以零等于 Infinity。这意味着,假如将代码从 JavaScript 迁移到 TypeScript ,即便 TypeScript 以为代码有类型过错,也能够确保以相同的方式运行。
  7. 对 JavaScript 类型进行了扩展,增加了例如 anyunknownnevervoid
  8. 一旦 TypeScript 的编译器完成了查看代码的作业,它就会 擦除 类型以生成终究的“已编译”代码。这意味着一旦代码被编译,生成的一般 JS 代码便没有类型信息。这也意味着 TypeScript 绝不会依据它揣度的类型更改程序的 行为。最重要的是,虽然您或许会在编译过程中看到类型过错,但类型体系本身与程序如何运行无关。
  9. 在较大型的项目中,能够在独自的文件 tsconfig.json 中声明 TypeScript 编译器的装备,并细化地调整其作业方式、严格程度、以及将编译后的文件存储在何处。

类型分配

创立变量时,TypeScript 有两种分配类型的方式:

  • 显式的 – 清晰写出类型。更易于阅览且更有目的性。
  • 隐式的 – 不写出类型,TypeScript 将依据分配的值“猜测”类型(称为 infer 类型推导)。分配更短,输入速度更快,而且经常在开发和测试时运用。
let firstName = "Dylan"; // 揣度为 string 类型
firstName = 33; // 现在赋值为 number 类型,报错

TypeScript 不能正确地揣度出变量的类型时,将设置类型为 any(禁用类型查看的类型)。

// json 为隐式 any 类型,由于 JSON.parse 不知道它回来什么类型的数据
let json = JSON.parse("55");
console.log(typeof json); // number
json = '1';
console.log(typeof json); // string

类型推导

  1. 在没有显式类型注释时运用类型揣度来供给类型信息。例如,下面隐式声明变量:
let x = 3;

变量的类型 x 将被揣度为 number,这种揣度发生在初始化变量和成员、设置参数默许值和确认函数回来类型时。

  1. 当从多个表达式进行类型揣度时,这些表达式的类型用于核算“最佳通用类型”。
let x = [0, 1, ''];

要揣度上例中 x 的类型,咱们有必要考虑每个数组元素的类型。在这里,咱们为数组类型供给了两种选择:numberstring,能够看到提示推导为 let x: (number | string)[]

  1. 在某些状况下类型共享公共结构,但没有一种类型是一切候选类型的超类型。
class Animal {}
class Rhino extends Animal {
  hasHorn: true;
}
class Elephant extends Animal {
  hasTrunk: true;
}
class Snake extends Animal {
  hasLegs: false;
}
let zoo = [new Rhino(), new Elephant(), new Snake()];

当没有找到“最佳通用类型”时,得到的揣度将是联合数组类型,能够看到提示推导为 let zoo: (Rhino | Elephant | Snake)[]

理想状况下,咱们或许期望 zoo 被揣度为 Animal[],但是由于数组中没有严格意义上的 Animal 类型的目标,所以咱们没有对数组元素类型进行揣度。为了纠正这一点,当没有一个类型是一切其他候选类型的超级类型时,就清晰地供给类型。

let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
  1. 当表达式的类型由其所在位置暗示时,就会呈现上下文类型化。例如:
window.onmousedown = function (mouseEvent) {
  console.log(mouseEvent.button);
  console.log(mouseEvent.kangaroo);
};

在这里,TypeScript 类型查看器经过 Window.onmousedown 事情能够揣度出 mouseEvent 参数的类型,该参数确实包括 button 特点,但不包括 kangaroo 特点。能够看到报错提示:Property 'kangaroo' does not exist on type 'MouseEvent'.。TypeScript 满足聪明,它也能够在其他上下文中揣度类型:

window.onscroll = function (uiEvent) {
  console.log(uiEvent.button);
};

TypeScript 知道 Window.onscroll 事情中参数 uiEvent 是一个 UIEvent,而不是像前面示例那样的 MouseEventUIEvent 目标不包括 button 特点,因此会抛出过错 Property 'button' does not exist on type 'Event'.

假如此函数不在上下文类型位置,则函数的参数将隐式具有类型 any,而且不会发出过错(除非运用 noImplicitAny 装备):

// @noImplicitAny: false
const handler = function (uiEvent) {
  console.log(uiEvent.button); // <- OK
};

咱们还能够显式地为函数的参数供给类型信息以掩盖任何上下文类型:

window.onscroll = function (uiEvent: any) {
  console.log(uiEvent.button); // 不报错,打印undefined,由于UIEvent目标不包括button特点
};

上下文类型化适用于许多状况。常见状况包括函数调用的参数、赋值的右侧、类型断语、目标和数组文字的成员以及回来语句。上下文类型也会充当“最佳通用类型”中的候选类型。例如:

function createZoo(): Animal[] {
  return [new Rhino(), new Elephant(), new Snake()];
}

在此示例中,“最佳通用类型”将从以下四个类型中选择:AnimalRhinoElephantSnake。终究,经过“最佳通用类型”算法Animal

数组

TypeScript 具有界说数组的特定语法。

  1. 在元素类型后面加上 []
const arr: number[] = [1, 2];
  1. 运用数组泛型。
const arr2: Array<number> = [1, 2];
  1. readonly 关键字能够避免数组内容被更改。
const arr: readonly number[] = [1, 2];
// arr.push(3); // Property 'push' does not exist on type 'readonly number[]'.
  1. 假如数组有值,TypeScript 能够揣度它的类型。
const numbers = [1, 2, 3]; // 揣度为类型 number[]
numbers.push(4); // OK
// numbers.push("2"); // Argument of type 'string' is not assignable to parameter of type 'number'

元组

数组中元素的数据类型都一般是相同的(any[] 类型的数组能够不同),假如存储的元素数据类型不同,则需求运用元组。

  1. 假如添加未界说的类型,会抛出过错;假如赋值时类型相同,顺序不同,同样会抛出过错。
let x: [string, number];
x = ['hi', 1];
// x.push(true); // Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
// x = [1, 'hi']; // Type 'number' is not assignable to type 'string'. Type 'string' is not assignable to type 'number'.
  1. readonly 关键字能够避免元组内容被更改。
let y: readonly [string, number] = ['hi', 1];
// y.push(undefined); // Property 'push' does not exist on type 'readonly [string, number]'.
  1. “命名元组”答应为咱们的索引值所代表的内容供给更多上下文。
const graph: [x: number, y: number] = [55.2, 41.3];
const [a, b] = graph;

object

  1. TypeScript 具有界说目标的特定语法。
const car: { type: string, model: string, year: number } = {
  type: "Toyota",
  model: "Corolla",
  year: 2009
};

目标类型能够独自写,也能够作为类型别号和接口重用。

  1. TypeScript 能够依据特点的值揣度特点的类型。
const car = {
  type: "Toyota",
};
car.type = "Ford";
// car.type = 2; // Type 'number' is not assignable to type 'string'.
  1. 可选特点是不必在目标界说中界说的特点。
constcar: { type: string, mileage?: number } = {// no error  
type:"Toyota"  
};  
car.mileage=2000;
  1. 索引签名可用于没有界说特点列表的目标。
const nameAgeMap: { [index: string]: number } = {};
nameAgeMap.Jack = 25; // no error
nameAgeMap.Mark = "Fifty"; // Error: Type 'string' is not assignable to type 'number'.

上述索引签名也能够经过运用东西类型 Record<string, number> 完成。

  1. Typescript 中的目标有必要是特定类型的实例。
const sites = {
  site1: "Runoob",
  site2: "Google",
};
sites.sayHello = function() {
  console.log("hello " + sites.site1);
};
sites.sayHello();

上面示例在目标上面没有对应的 sayHello 类型界说,将不能进行特点赋值,会呈现编译过错,:Property 'sayHello' does not exist on type '{ site1: string; site2: string; }'.,所以有必要在目标上面界说类型模板。

const sites = {
  site1: "Runoob",
  site2: "Google",
  sayHello: function() {}
};

null 和 undefined

默许状况下,类型查看器以为nullundefined 能够赋值给任何类型,即 nullundefined 是一切其它类型的一个有效值。除非在 tsconfig.json 中设置 strictNullCheckstrue,当你声明一个变量时,它不会主动地包括nullundefined,但能够运用联合类型清晰的包括它们,例如 string | null

  1. nullundefined 是原始类型,能够像字符串等其他类型相同运用。
let y: undefined = undefined;
console.log(typeof y);
let z: null = null;
console.log(typeof z);
  1. 可选链 ?. 是一种原生 JavaScript 特性,能够很好地与 TypeScript 中的 nullundefined 配合运用。
interface House {
  sqft: number;
  yard?: {
    sqft: number;
  };
}
function printYardSize(house: House) {
  const yardSize = house.yard?.sqft;
  if (yardSize === undefined) {
    console.log('No yard');
  } else {
    console.log(`Yard is ${yardSize} sqft`);
  }
}
let home: House = {
  sqft: 500
};
printYardSize(home); // 'No yard'
  1. 空值兼并 ?? 是另一个 JavaScript 特性,它也能够很好地与 TypeScript 的空值处理配合运用。
function printMileage(mileage: number | null | undefined) {
  console.log(`Mileage: ${mileage ?? 'Not Available'}`);
}
printMileage(null); // 'Mileage: Not Available'
printMileage(0); // 'Mileage: 0'
  1. TypeScript 还有一种特别的空值断语语法,能够在不进行任何显式查看的状况下从类型中删除 nullundefined。在任何表达式之后写 !,表明该值不是 nullundefined
function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

就像其他类型断语相同,这不会改动代码的运行时行为,所以只有当您知道该值不能为 nullundefined 时运用 !

  1. 即便启用了 strictNullChecks,默许状况下 TypeScript 也会假定数组访问永远不会回来 undefined(除非 undefined 是数组类型的一部分)。经过装备 noUncheckedIndexedAccess 可用于更改此行为。
let array: number[] = [1, 2, 3];
let value = array[0]; // `number | undefined` "noUncheckedIndexedAccess": true

特别类型

TypeScript 具有或许不引用任何特定类型数据的特别类型。

any

any 是一种禁用类型查看并有效地答应运用一切类型的类型。any 类型是一种消除过错的有用办法,由于它禁用了类型查看,但 TypeScript 将无法供给类型安全,而且依靠类型数据的东西(例如主动完成)将无法作业。所以,咱们应尽量避免运用它。

以下三种状况能够运用 any 类型。

  1. 变量的值会动态改动时,比如来自用户的输入。
let x: any = 1; // 数字类型
x = 'I am who I am'; // 字符串类型
x = false; // 布尔类型
  1. 改写现有代码时,恣意值答应在编译时可选择地包括或移除类型查看。
let v: any = true;
v = "string"; // 没有过错
v.ifItExists(); // ifItExists 办法在运行时或许不存在而报错,但这里并不会查看
console.log(Math.round(v)); // 没有过错
  1. 界说存储各种类型数据的数组。
const arrayList: any[] = [1, false, 'fine'];
arrayList[1] = 100;

unknown

unknown 类型表明任何值,类似于 any 类型,但更安全,由于用未知值做任何事情都是不合法的。由于 any 违背了类型查看的初衷,一般不主张运用,特别在有了 unknown 类型之后。

  1. 任何类型能够分配给 anyunknownany 能够分配给任何类型,unknown 只能分配给 unknown 或许 any
let a: any;
let n: number;
let w: unknown = 1; 
w = "string";
w = a;
a = w;
n = a;
// n = w; // Type 'unknown' is not assignable to type 'number'
  1. 函数回来值类型为 unknown 时要有 return 语句。
function f(): unknown {} // A function whose declared type is neither 'void' nor 'any' must return a value
  1. 当不知道输入的数据类型时,最好运用 unknown。要稍后添加类型,需求强制转化它。
const w = {
  runANonExistentMethod: () => {
    console.log("I think therefore I am");
  }
} as { runANonExistentMethod: () => void };
if(typeof w === 'object' && w !== null) {
  (w as { runANonExistentMethod: Function }).runANonExistentMethod(); 
}

never

never 代表从不会呈现的值,在函数中它通常表现为抛出异常或无法履行到终止点(例如无限循环)。never 很少独自运用,它的主要用途是在高档泛型中。

function error(message: string): never {
  throw new Error(message);
}
function loop(): never {
  while (true) {}
}

void

表明函数没有任何回来语句,或许不从这些回来语句回来任何显式值。在 JavaScript 中,不回来任何值的函数将隐式回来值 undefined。但是,void 和回来 undefined 在 TypeScript 中不是一回事。

function hello(): void {
  console.log("Hello");
  return true; // Type 'boolean' is not assignable to type 'void'.
}
hello();

函数类型 type vf = () => void 在完成时能够回来任何其他值,以下类型的完成是有效的:

type voidFunc = () => void;
const f1: voidFunc = () => {
  return true;
};
const f2: voidFunc = () => true;
const f3: voidFunc = function() {
  return true;
};
const v1 = f1();
const v2 = f2();
const v3 = f3();
console.log(v1, v2, v3); // true true true