TypeScript 完全手册10bet

来源:http://www.chinese-glasses.com 作者:Web前端 人气:53 发布时间:2020-03-24
摘要:时间: 2019-11-07阅读: 64标签: 字面量 时间: 2019-08-23阅读: 89标签: 手册 什么是 TypeScript? TypeScript 2.1引入了映射类型,这是对类型系统的一个强大的补充。本质上,映射类型允许w咱们通过

时间: 2019-11-07阅读: 64标签: 字面量

时间: 2019-08-23阅读: 89标签: 手册什么是 TypeScript?

TypeScript 2.1 引入了映射类型,这是对类型系统的一个强大的补充。本质上,映射类型允许w咱们通过映射属性类型从现有类型创建新类型。根据咱们指定的规则转换现有类型的每个属性。转换后的属性组成新的类型。

TypeScript 是 JavaScript 的超集,具有静态类型特性,旨在简化大型 JavaScript 应用程序的开发,也被称为JavaScript that scales可拓展的 JavaScript)。

使用映射类型,可以捕获类型系统中类似Object.freeze()等方法的效果。冻结对象后,就不能再添加、更改或删除其中的属性。来看看如何在不使用映射类型的情况下在类型系统中对其进行编码:

为什么要用 TypeScript?

interface Point { x: number; y: number;}interface FrozenPoint { readonly x: number; readonly y: number;}function freezePoint(p: Point): FrozenPoint { return Object.freeze(p);}const origin = freezePoint({ x: 0, y: 0 });// Error! Cannot assign to 'x' because it// is a constant or a read-only property.origin.x = 42;

JavaScript 在过去几年中快速发展,成为客户端和服务器端最通用的跨平台语言。

咱们定义了一个包含x和y两个属性的Point接口,咱们还定义了另一个接口FrozenPoint,它与Point相同,只是它的所有属性都被使用readonly定义为只读属性。

但 JavaScript 本意并不用于大型应用开发。它是一种没有类型系统的动态语言,也就是说,变量的值可以是任何类型(例如字符串或布尔值)。

freezePoint函数接受一个Point作为参数并冻结该参数,接着,向调用者返回相同的对象。然而,该对象的类型已更改为FrozenPoint,因此其属性被静态类型化为只读。这就是为什么当试图将42赋值给x属性时,TypeScript会出错。在运行时,分配要么抛出一个类型错误(严格模式),要么静默失败(非严格模式)。

而类型系统能够提高代码质量和可读性,使代码库更易于维护或重构。更重要的是它可以在编译时就捕获错误,而不是在运行时才捕获。

虽然上面的示例可以正确地编译和工作,但它有两大缺点

而 JavaScript 并没有类型系统,所以一个大型开发团队难以使用 JavaScript 构建复杂的应用程序。

需要两个接口。除了Point类型之外,还必须定义FrozenPoint类型,这样才能将readonly修饰符添加到两个属性中。当咱们更改Point时,还必须更改FrozenPoint,这很容易出错,也很烦人。需要 freezePoint 函数。对于希望在应用程序中冻结的每种类型的对象,咱们就必须定义一个包装器函数,该函数接受该类型的对象并返回冻结类型的对象。没有映射类型,咱们就不能以通用的方式静态地使用Object.freeze()。使用映射类型构建 Object.freeze()

而 TypeScript 能在编译时检查不同部分代码的正确性。在编译时检查出错误,便于开发者发现错误的位置和具体问题。如果运行时才检查出错误,则需要跟踪复杂的堆栈,花费大量时间进行调试。

来看看Object.freeze()是如何在lib.d.ts文件中定义的:

TypeScript 的优点在开发周期中能更早地捕获潜在的错误。管理大型代码库。更易于重构。更易于团队合作:代码的耦合性越强,不同开发人员访问代码库时越不容易造成无意破坏。文档特性:类型本身就是一种文档信息,方便日后开发者本人或者其他开发者查询。TypeScript 的缺点需要额外的学习:需要在短期放缓进度与长期提高效率间进行权衡。类型错误可能多种多样。配置极大地影响运行。类型Boolean (布尔值)

/** * Prevents the modification of existing property attributes and values, and prevents the addition of new properties. * @param o Object on which to lock the attributes. */freezeT(o: T): ReadonlyT;
const isLoading: boolean = false;

该方法的返回类型为ReadonlyT,这是一个映射类型,它的定义如下:

Number (数字)

type ReadonlyT = { readonly [P in keyof T]: T[P]};
const decimal: number = 8;const binary: number = 0b110;

这个语法一开始可能会让人望而生畏,咱们来一步一步分析它:

String (字符串)

用一个名为T的类型参数定义了一个泛型 Readonly。在方括号中,使用了keyof操作符。keyof T将T类型的所有属性名表示为字符串字面量类型的联合。方括号中的in关键字表示我们正在处理映射类型。[P in keyof T]: T[P]表示将T类型的每个属性P的类型转换为T[P]。如果没有readonly修饰符,这将是一个身份转换。类型T[P]是一个查找类型,它表示类型T的属性P的类型。最后,readonly修饰符指定每个属性都应该转换为只读属性。

const fruit: string = "orange";

因为ReadonlyT类型是泛型的,所以咱们为T提供的每种类型都正确地入了Object.freeze()中。

Array (数组)

const origin = Object.freeze({ x: 0, y: 0 });// Error! Cannot assign to 'x' because it// is a constant or a read-only property.origin.x = 42;

数组可以写成下面两种形式:

映射类型的语法更直观解释

// 最常见的方式let firstFivePrimes: number[] = [2, 3, 5, 7, 11];// 不太常见的方式:使用泛型 (稍后介绍)let firstFivePrimes2: Arraynumber = [2, 3, 5, 7, 11];

这次咱们使用Point类型为例来粗略解释类型映射如何工作。请注意,以下只是出于解释目的,并不能准确反映TypeScript使用的解析算法。

Tuple (元组)

从类型别名开始:

Tuple 类型表示一种组织好的数组,元素的类型预先知道,并且数量固定。这意味着你有可能得到错误提示:

type ReadonlyPoint = ReadonlyPoint;
let contact: [string, number] = ['John', 954683];contact = ['Ana', 842903, 'extra argument'] /* Error! Type '[string, number, string]' is not assignable to type '[string, number]'. */

现在,咱们可以在ReadonlyT中为泛型类型T的替换Point类型:

Any (任意值)

type ReadonyPoint = { readonly [P in keyof Point]: Point[P]};

any与类型系统中的任何类型都兼容。意味着可以将任何内容赋值给它,也可以将它赋值给任何类型。它能让你避开类型检查。

现在咱们知道T是Point,可以确定keyof Point表示的字符串字面量类型的并集:

let variable: any = 'a string';variable = 5;variable = false;variable.someRandomMethod(); /* 行吧,也许运行的时候 someRandomMethod 是存在的 */
type ReadonlyPoint = { readonly [P in "x" | "y"]: Point[p]};

Void (空值)

类型P表示每个属性x和y,咱们把它们作为单独的属性来写,去掉映射的类型语法

void表示没有任何类型。它通常用作没有返回值的函数的返回类型。

type ReadonlyPoint = { readonly x: Point["x"]; readonly y: Point["y"];}; 
function sayMyName(name: string): void { console.log(name);}sayMyName('Heisenberg');

最后,咱们可以解析这两种查找类型,并将它们替换为具体的x和y类型,这两种类型都是number。

Never

type ReadonlyPoint = { readonly x: number; readonly y: number;};

never类型表示的是那些永不存在的值的类型。 例如,never类型是那些总是会抛出异常、或者根本就不会有返回值的函数的返回值类型。

最后,得到的ReadonlyPoint类型与咱们手动创建的FrozenPoint类型相同。

// 抛出异常function error(message: string): never { throw new Error(message);}// 永远不能返回function continuousProcess(): never { while (true) { // ... }}

更多映射类型的示例

Null 和 Undefined

上面已经看到lib.d.ts文件中内置的Readonly T类型。此外,TypeScript 定义了其他映射类型,这些映射类型在各种情况下都非常有用。如下:

undefined和null两者各自有自己的类型分别叫做undefined和null。和void相似,它们的本身的类型用处不是很大,但是在联合类型中非常有用(稍后介绍)

/** * Make all properties in T optional */type PartialT = { [P in keyof T]?: T[P]};/** * From T pick a set of properties K */type PickT, K extends keyof T = { [P in K]: T[P]};/** * Construct a type with a set of properties K of type T */type RecordK extends string, T = { [P in K]: T};
type someProp = string | null | undefined;

这里还有两个关于映射类型的例子,如果需要的话,可以自己编写:

Unknown

/** * Make all properties in T nullable */type NullableT = { [P in keyof T]: T[P] | null};/** * Turn all properties of T into strings */type StringifyT = { [P in keyof T]: string};

TypeScript 3.0 引入了 unknown (未知) 类型,它是与any类型对应的安全类型。任何东西都可以赋值给unknown,但unknown不能赋值给除了它本身和any以外的任何东西。在没有先断言或指定到更具体类型的情况下,不允许对unknown进行任何操作。

映射类型和联合的组合也是很有趣:

type I1 = unknown  null; // nulltype I2 = unknown  string; // stringtype U1 = unknown | null; // unknowntype U2 = unknown | string; // unknown
type X = ReadonlyNullableStringifyPoint;// type X = {// readonly x: string | null;// readonly y: string | null;// };

类型别名

映射类型的实际用例

类型别名可以为现有类型提供替代名称,以便某些地方使用。构造它的语法如下:

实战中经常可以看到映射类型,来看看 React 和 Lodash :

type Login = string;

React:组件的setState方法允许咱们更新整个状态或其中的一个子集。咱们可以更新任意多个属性,这使得setState方法成为PartialT的一个很好的用例。Lodash:pick函数从一个对象中选择一组属性。该方法返回一个新对象,该对象只包含咱们选择的属性。可以使用PickT对该行为进行构建,正如其名称所示。更好的字面量类型推断

联合类型

字符串、数字和布尔字面量类型(如:"abc",1和true)之前仅在存在显式类型注释时才被推断。从 TypeScript 2.1 开始,字面量类型总是推断为默认值。在 TypeScript 2.0 中,类型系统扩展了几个新的字面量类型:

TypeScript 允许让一个属性具有多种数据类型,名为 union (联合) 类型。

boolean字面量类型数字字面量枚举字面量

type Password = string | number;

不带类型注解的const变量或readonly属性的类型推断为字面量初始化的类型。已经初始化且不带类型注解的let变量、var变量、形参或非readonly属性的类型推断为初始值的扩展字面量类型。字符串字面量扩展类型是string,数字字面量扩展类型是number,true或false的字面量类型是boolean,还有枚举字面量扩展类型是枚举。

交叉类型

更好的 const 变量推断

交叉类型是将多种类型叠加到一起成为一种类型。

咱们从局部变量和var关键字开始。当TypeScript看到下面的变量声明时,它会推断baseUrl变量的类型是string:

interface Person { name: string; age: number;}interface Worker { companyId: string;}type Employee = Person  Worker;
var baseUrl = "";// 推断类型: string

Interface (接口)

用let关键字声明的变量也是如此

接口好似你和编译器定义契约,由你指定一个类型,预期它的属性应该是些什么类型。

let baseUrl = "";// 推断类型: string

边注:接口不受 JavaScript 运行时的特性影响,它只在类型检查中会用到。

这两个变量都推断为string类型,因为它们可以随时更改。它们是用一个字面量字符串值初始化的,但是以后可以修改它们。

可以声明可选属性(带有?标记),意味着接口的对象可能会、也可能不会定义这些属性。可以声明只读属性,意味着一旦为属性赋值,就无法更改。

但是,如果使用const关键字声明变量并使用字符串字面量进行初始化,则推断的类型不再是string,而是字面量类型:

interface ICircle { readonly id: string; center: { x: number; y: number; }, radius: number; color?: string; // 可选属性}const circle1: ICircle = { id: '001', center: { x: 0 }, radius: 8,}; /* Error! Property 'y' is missing in type '{ x: number; }' but required in type '{ x: number; y: number; }'. */const circle2: ICircle = { id: '002', center: { x: 0, y: 0 }, radius: 8,} // 正确
const baseUrl = "";// 推断类型: ""

扩展接口

由于常量字符串变量的值永远不会改变,因此推断出的类型会更加的具体。baseUrl变量无法保存""以外的任何其他值。

接口可以扩展成另一个接口,或者更多接口。这使得接口的编写更具有灵活性和复用性。

字面量类型推断也适用于其他原始类型。如果用直接的数值或布尔值初始化常量,推断出的还是字面量类型:

interface ICircleWithArea extends ICircle { getArea: () = number;}
const HTTPS_PORT = 443;// 推断类型: 443const rememberMe = true;// 推断类型: true

实现接口

类似地,当初始化器是枚举值时,推断出的也是字面量类型:

实现接口的类需要严格遵循接口的结构。

enum FlexDirection { Row, Column}const direction = FlexDirection.Column;// 推断类型: FlexDirection.Column
interface IClock { currentTime: Date; setTime(d: Date): void;}

注意,direction类型为FlexDirection.Column,它是枚举字面量类型。如果使用let或var关键字来声明direction变量,那么它的推断类型应该是FlexDirection。

枚举

更好的只读属性推断

enum(枚举) 用来组织一组的相关值,这些值可以是数值,也可以是字符串值。

与局部const变量类似,带有字面量初始化的只读属性也被推断为字面量类型:

enum CardSuit { Clubs, Diamonds, Hearts, Spades}let card = CardSuit.Clubs;
class ApiClient { private readonly baseUrl = ""; // 推断类型: "" get(endpoint: string) { // ... }}

默认情况下,枚举的本质是数字。enum的取值从 0 开始,以 1 递增。

只读类属性只能立即初始化,也可以在构造函数中初始化。试图更改其他位置的值会导致编译时错误。因此,推断只读类属性的字面量类型是合理的,因为它的值不会改变。

上一个例子所生成的 JavaScript 代码如下:

当然,TypeScript 不知道在运行时发生了什么:用readonly标记的属性可以在任何时候被一些JS 代码改变。readonly修饰符只限制从TypeScript代码中对属性的访问,在运行时就无能为力。也就是说,它会被编译时删除掉,不会出现在生成的 JS 代码中。

var CardSuit;(function (CardSuit) { CardSuit[CardSuit["Clubs"] = 0] = "Clubs"; CardSuit[CardSuit["Diamonds"] = 1] = "Diamonds"; CardSuit[CardSuit["Hearts"] = 2] = "Hearts"; CardSuit[CardSuit["Spades"] = 3] = "Spades";})(CardSuit || (CardSuit = {}));

推断字面量类型的有用性

或者,枚举可以用字符串值来初始化,这种方法更易读。

你可能会问自己,为什么推断const变量和readonly属性为字面量类型是有用的。考虑下面的代码:

enum SocialMedia { Facebook = 'FACEBOOK', Twitter = 'TWITTER', Instagram = 'INSTAGRAM', LinkedIn = 'LINKEDIN'}
const HTTP_GET = "GET"; // 推断类型: "GET"const HTTP_POST = "POST"; // 推断类型: "POST"function get(url: string, method: "GET" | "POST") { // ...}get("", HTTP_GET);

反向映射

如果推断HTTP_GET常量的类型是string而不是“GET”,则会出现编译时错误,因为无法将HTTP_GET作为第二个参数传递给get函数:

enum支持反向映射,也就是说,可以通过值来获得成员、成员名。

Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'

回顾之前 CardSuit 的例子:

当然,如果相应的参数只允许两个特定的字符串值,则不允许将任意字符串作为函数参数传递。但是,当为两个常量推断字面量类型“GET”和“POST”时,一切就都解决了。

const clubsAsNumber: number = CardSuit.Clubs; // 3const clubsAsString: string = CardSuit[0]; // 'Clubs'

原文:-types-in-typescript

函数

你可以为每个参数指定一个类型,再为函数指定一个返回类型。

function add(x: number, y: number): number { return x + y;}

函数重载

TypeScript 允许声明函数重载。简单来说,可以使用多个名称相同但参数类型和返回类型不同的函数。参考下面的例子:

function padding(a: number, b?: number, c?: number, d?: any) { if (b === undefined  c === undefined  d === undefined) { b = c = d = a; } else if (c === undefined  d === undefined) { c = a; d = b; } return { top: a, right: b, bottom: c, left: d };}

参数的含义根据传递给函数的参数数量而变化。此外,该函数只接受一个、两个或四个参数。要构造函数重载,只需多次声明函数头就可以了。最后一个函数头真正实现了函数体,但函数外部并不能直接调用最后一个函数头。

function padding(all: number);function padding(topAndBottom: number, leftAndRight: number);function padding(top: number, right: number, bottom: number, left: number);function padding(a: number, b?: number, c?: number, d?: number) { if (b === undefined  c === undefined  d === undefined) { b = c = d = a; } else if (c === undefined  d === undefined) { c = a; d = b; } return { top: a, right: b, bottom: c, left: d };}

你可以指定属性的类型和方法参数的类型。

class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet(name: string) { return `Hi ${name}, ${this.greeting}`; }}

访问修饰符

本文由10bet发布于Web前端,转载请注明出处:TypeScript 完全手册10bet

关键词:

上一篇:CSS元素隐藏

下一篇:没有了

频道精选

最火资讯