Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript 之 Keyof Type Operator #223

Open
mqyqingfeng opened this issue Nov 23, 2021 · 14 comments
Open

TypeScript 之 Keyof Type Operator #223

mqyqingfeng opened this issue Nov 23, 2021 · 14 comments

Comments

@mqyqingfeng
Copy link
Owner

mqyqingfeng commented Nov 23, 2021

前言

TypeScript 的官方文档早已更新,但我能找到的中文文档都还停留在比较老的版本。所以对其中新增以及修订较多的一些章节进行了翻译整理。

本篇整理自 TypeScript Handbook 中 「Keyof Type Operator」 章节。

本文并不严格按照原文翻译,对部分内容也做了解释补充。

keyof 类型操作符

对一个对象类型使用 keyof 操作符,会返回该对象属性名组成的一个字符串或者数字字面量的联合。这个例子中的类型 P 就等同于 "x" | "y":

type Point = { x: number; y: number };
type P = keyof Point;

// type P = keyof Point

但如果这个类型有一个 string 或者 number 类型的索引签名,keyof 则会直接返回这些类型:

type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish;
// type A = number

type Mapish = { [k: string]: boolean };
type M = keyof Mapish;
// type M = string | number

注意在这个例子中,Mstring | number,这是因为 JavaScript 对象的属性名会被强制转为一个字符串,所以 obj[0]obj["0"] 是一样的。

(注:原文到这里就结束了)

数字字面量联合类型

在一开始我们也说了,keyof 也可能返回一个数字字面量的联合类型,那什么时候会返回数字字面量联合类型呢,我们可以尝试构建这样一个对象:

const NumericObject = {
  [1]: "冴羽一号",
  [2]: "冴羽二号",
  [3]: "冴羽三号"
};

type result = keyof typeof NumericObject

// typeof NumbericObject 的结果为:
// {
//   1: string;
//   2: string;
//   3: string;
// }
// 所以最终的结果为:
// type result = 1 | 2 | 3

Symbol

其实 TypeScript 也可以支持 symbol 类型的属性名:

const sym1 = Symbol();
const sym2 = Symbol();
const sym3 = Symbol();

const symbolToNumberMap = {
  [sym1]: 1,
  [sym2]: 2,
  [sym3]: 3,
};

type KS = keyof typeof symbolToNumberMap; // typeof sym1 | typeof sym2 | typeof sym3

这也就是为什么当我们在泛型中像下面的例子中使用,会如此报错:

function useKey<T, K extends keyof T>(o: T, k: K) {
  var name: string = k; 
  // Type 'string | number | symbol' is not assignable to type 'string'.
}

如果你确定只使用字符串类型的属性名,你可以这样写:

function useKey<T, K extends Extract<keyof T, string>>(o: T, k: K) {
  var name: string = k; // OK
}

而如果你要处理所有的属性名,你可以这样写:

function useKey<T, K extends keyof T>(o: T, k: K) {
  var name: string | number | symbol = k;
}

类和接口

对类使用 keyof

// 例子一
class Person {
  name: "冴羽"
}

type result = keyof Person;
// type result = "name"
// 例子二
class Person {
  [1]: string = "冴羽";
}

type result = keyof Person;
// type result = 1

对接口使用 keyof

interface Person {
  name: "string";
}

type result = keyof Person;
// type result = "name"

实战

在「TypeScript 之 Generic」这篇中就讲到了一个 keyof 的应用:

我们希望获取一个对象给定属性名的值,为此,我们需要确保我们不会获取 obj 上不存在的属性。所以我们在两个类型之间建立一个约束:

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}
 
let x = { a: 1, b: 2, c: 3, d: 4 };
 
getProperty(x, "a");
getProperty(x, "m");

// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

在后面的「Mappred Types」 章节中,我们还会讲到 keyof

TypeScript 系列

TypeScript 系列文章由官方文档翻译、重难点解析、实战技巧三个部分组成,涵盖入门、进阶、实战,旨在为你提供一个系统学习 TS 的教程,全系列预计 40 篇左右。点此浏览全系列文章,并建议顺便收藏站点。

微信:「mqyqingfeng」,加我进冴羽唯一的读者群。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

@slogeor
Copy link

slogeor commented Jan 29, 2022

打卡

@yangliguo7
Copy link

这和ts 无关,但是请问下,为什么

const NumericObject = {
  [1]: "冴羽一号",
  [2]: "冴羽二号",
  [3]: "冴羽三号"
};

这里 [1] 可以做对象的key,而像[1,2]却不能直接作为对象的key呢?
例如

const NumericObject = {
  [1,2]: "冴羽一号",
};

@shiyiya
Copy link

shiyiya commented Feb 10, 2022

const o = {
  [这里是变量]: value
}

1,2 显然不是一个正确的变量...

@yangliguo7
Copy link

const o = {
  [这里是变量]: value
}

1,2 显然不是一个正确的变量...

可是为什么1却又可以了呢 ?

@Gip886
Copy link

Gip886 commented Apr 2, 2022

@YangLG-7

const o = {
  [这里是变量]: value
}

1,2 显然不是一个正确的变量...

可是为什么1却又可以了呢 ?

[]:这里里面可以填,变量:string | number 类型 ,填写1属于number类型合法, 填写[1,2] 不属于string | number ,属于number[] 不合法

@shjames
Copy link

shjames commented Apr 29, 2022

'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later 报这个错怎么解决? 修改了target和lib也不行

@misty0304
Copy link

typeof NumbericObject 的结果为:不应该是 'object' 么?

@zhangyachang
Copy link

'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later 报这个错怎么解决? 修改了target和lib也不行

这个在目录下新建一个tsconfig.json的文件,将target修改为es6即可。

@zhangyachang
Copy link

'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later 报这个错怎么解决? 修改了target和lib也不行

{
"compilerOptions": {
"target": "ES6"
}
}

@PleasePerfunctory
Copy link

日常膜拜羽哥

@YuFengjie97
Copy link

YuFengjie97 commented Sep 29, 2022

typeof NumbericObject 的结果为:
{
1: string;
2: string;
3: string;
},
为什么typeof返回结果是这样的,不应该是个"object" 吗?
哦,输出是"object",只是代码提示才是上面那样
哦,typeof在下章

@TLovers
Copy link

TLovers commented Jan 22, 2023

@Gip886

@YangLG-7

const o = {
  [这里是变量]: value
}

1,2 显然不是一个正确的变量...

可是为什么1却又可以了呢 ?

[]:这里里面可以填,变量:string | number 类型 ,填写1属于number类型合法, 填写[1,2] 不属于string | number ,属于number[] 不合法

不赞同这种解析,这很明显是JS语法问题,怎么能扯到Ts,属性名表达式 顾名思义是一个计算出一个值, 1 是值 1,2 不是值啊

@TLovers
Copy link

TLovers commented Jan 22, 2023

@yangliguo7

这和ts 无关,但是请问下,为什么

const NumericObject = {
  [1]: "冴羽一号",
  [2]: "冴羽二号",
  [3]: "冴羽三号"
};

这里 [1] 可以做对象的key,而像[1,2]却不能直接作为对象的key呢?
例如

const NumericObject = {
  [1,2]: "冴羽一号",
};

个人理解是JS语法规定问题,对象的key使用[] 是属性名表达式的一种写法,表达式肯定是得出某个值
你甚至可以这样玩

const NumericObject = {
[1+2]: "冴羽一号",
};

@lsc9
Copy link

lsc9 commented Apr 13, 2023

@TLovers

@yangliguo7

这和ts 无关,但是请问下,为什么

const NumericObject = {
  [1]: "冴羽一号",
  [2]: "冴羽二号",
  [3]: "冴羽三号"
};

这里 [1] 可以做对象的key,而像[1,2]却不能直接作为对象的key呢?
例如

const NumericObject = {
  [1,2]: "冴羽一号",
};

个人理解是JS语法规定问题,对象的key使用[] 是属性名表达式的一种写法,表达式肯定是得出某个值
你甚至可以这样玩

const NumericObject = {
[1+2]: "冴羽一号",
};

是 ES6 的语法,[] 是属性表达式语法,里面放的是表达式。
1 是正确的表达式;而 1,2 不是所以抛出异常。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests