纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

TS映射类型和字面量类型推断 详解TypeScript映射类型和更好的字面量类型推断

浅笑·   2021-05-07 我要评论
想了解详解TypeScript映射类型和更好的字面量类型推断的相关内容吗浅笑·在本文为您仔细讲解TS映射类型和字面量类型推断的相关知识和一些Code实例欢迎阅读和指正我们先划重点:TS,映射类型,TS,字面量下面大家一起来学习吧

概述

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

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

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;

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

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

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

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

使用映射类型构建 Object.freeze()

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

/**
  * 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.
  */
freeze<T>(o: T): Readonly<T>;

该方法的返回类型为Readonly<T>这是一个映射类型它的定义如下:

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
};

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

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

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

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;

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

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

从类型别名开始:

type ReadonlyPoint = Readonly<Point>;

现在咱们可以在Readonly<T>中为泛型类型T的替换Point类型:

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

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

type ReadonlyPoint = {
  readonly [P in "x" | "y"]: Point[p]
};

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

type ReadonlyPoint = {
  readonly x: Point["x"];
  readonly y: Point["y"];
};   

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

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

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

更多映射类型的示例

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

/**
 * Make all properties in T optional
 */
type Partial<T> = {
  [P in keyof T]?: T[P]
};

/**
 * From T pick a set of properties K
 */
type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
};

/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends string, T> = {
  [P in K]: T
};

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

/**
 * Make all properties in T nullable
 */
type Nullable<T> = {
  [P in keyof T]: T[P] | null
};

/**
 * Turn all properties of T into strings
 */
type Stringify<T> = {
  [P in keyof T]: string
};

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

type X = Readonly<Nullable<Stringify<Point>>>;
// type X = {
//     readonly x: string | null;
//     readonly y: string | null;
// };

映射类型的实际用例

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

  • react:组件的setState方法允许咱们更新整个状态或其中的一个子集咱们可以更新任意多个属性这使得setState方法成为Partial<T>的一个很好的用例
  • Lodash:pick函数从一个对象中选择一组属性该方法返回一个新对象该对象只包含咱们选择的属性可以使用Pick<T>对该行为进行构建正如其名称所示

更好的字面量类型推断

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

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

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

更好的 const 变量推断

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

var baseUrl = "https://example.com/";
// 推断类型: string

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

let baseUrl = "https://example.com/";
// 推断类型: string

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

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

const baseUrl = "https://example.com/";
// 推断类型: "https://example.com/"

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

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

const HTTPS_PORT = 443;
// 推断类型: 443

const rememberMe = true;
// 推断类型: true

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

enum FlexDirection {
  Row,
  Column
}

const direction = FlexDirection.Column;
// 推断类型: FlexDirection.Column

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

更好的只读属性推断

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

class ApiClient {
  private readonly baseUrl = "https://api.example.com/";
  // 推断类型: "https://api.example.com/"

  get(endpoint: string) {
    // ...
  }
}

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

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

推断字面量类型的有用性

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

const HTTP_GET = "GET"; // 推断类型: "GET"
const HTTP_POST = "POST"; // 推断类型: "POST"

function get(url: string, method: "GET" | "POST") {
  // ...
}

get("https://example.com/", HTTP_GET);

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

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

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


相关文章

猜您喜欢

  • Vue seo优化 详解Vue开发网站seo优化方法

    想了解详解Vue开发网站seo优化方法的相关内容吗龖龖龖在本文为您仔细讲解Vue seo优化 的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Vue,seo优化,Vue,网站优化下面大家一起来学习吧..
  • asp.net core token的认证 asp.net core MVC之实现基于token的认证

    想了解asp.net core MVC之实现基于token的认证的相关内容吗CN_Simo在本文为您仔细讲解asp.net core token的认证的相关知识和一些Code实例欢迎阅读和指正我们先划重点:asp.net,core,token,认证,asp.net,MVC,验证Token下面大家一起来学习吧..

网友评论

Copyright 2020 www.fresh-weather.com 【世纪下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式