首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类型记录:映射类型中的索引签名

类型记录:映射类型中的索引签名
EN

Stack Overflow用户
提问于 2020-11-23 13:23:00
回答 2查看 1.2K关注 0票数 9

如何在'k'number上使用'k'和抽象类型的number?我希望有一个类型别名T,这样T<'k', number>就会给出该类型。

请考虑以下示例:

代码语言:javascript
复制
function f(x: { 'k': number, [s: string]: any }) {}                           // ok
type T_no_params = { 'k': number, [s: string]: any };                         // ok
type T_key_only<k extends string> = { [a in k]: number };                     // ok
type T_value_only<V> = { 'k': V, [s: string]: any};                           // ok
type T_key_and_index<k extends string, V> = { [a in k]: V, [s: string]: any };// ?
  • 直接使用{ 'k': number, [s: string]: any}作为函数f的参数类型是可行的。
  • [s: string]: any -alias works中使用type索引部件
  • k extends string中使用type-alias也是有效的
  • 当将k extends string[s: string]: any在同一个type-alias中组合时,我会得到一个解析错误(甚至不是语义错误,它甚至看起来都不是有效的语法)。

这在这里似乎是可行的:

代码语言:javascript
复制
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }

但在这里,我不太明白为什么它不抱怨额外的属性( &右侧的类型不应该允许具有额外属性的对象)。

编辑

在回答中曾多次提到&是相交算子,它的行为应该与集合论交点相似。然而,在处理额外属性时,情况并非如此,下面的示例说明了这一点:

代码语言:javascript
复制
function f(x: {a: number}){};
function g(y: {b: number}){};
function h(z: {a: number} & {b: number}){};

f({a: 42, b: 58});  // does not compile. {a: 42, b: 58} is not of type {a: number}
g({a: 42, b: 58});  // does not compile. {a: 42, b: 58} is not of type {b: number}
h({a: 42, b: 58});  // compiles!

在本例中,似乎{a: 42, b: 58}既不是{a: number}类型,也不是{b: number}类型,但它以某种方式在交叉{a: number} & {b: number}中结束。这不是集合理论交叉口的工作方式。

这正是我自己的&-proposal看上去如此可疑的原因。如果有人能详细说明如何将映射的类型与{ [s: string]: any }“相交”使类型“更大”而不是使其更小,我将不胜感激。

我见过这些问题

但这些似乎没有直接的关系,尽管有一个相似的名字。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-01 11:58:55

type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }是定义您所追求的类型的正确方法。但是有一件事需要知道(转述弃用标志: keyofStringsOnly):

当将keyof类型运算符应用于具有字符串索引签名的类型时,将返回字符串编号而不是字符串。

我不知道有什么方法可以将索引限制为string类型而不是string | number类型。实际上,允许number访问string索引似乎是一件合理的事情,因为它与Javascript的工作原理是一致的(人们总是可以压缩一个数字)。另一方面,您无法安全地访问具有字符串值的数字索引。

&类型操作符类似于集合论交集-它总是限制可能的值集(或者保持不变,但从不扩展)。在您的情况下,类型排除任何非字符串类键作为索引。准确地说,您将unique symbol排除为索引。

我认为您的困惑可能来自于类型记录如何对待函数参数。使用显式定义的参数调用函数与将参数作为变量传递不同。在这两种情况下,类型记录确保所有参数都是正确的结构/形状,但在后一种情况下,它另外不允许额外的道具。

说明这些概念的代码:

代码语言:javascript
复制
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V };
type WithNumber = HasKeyValue<"n", number>;
const x: WithNumber = {
  n: 1
};

type T = keyof typeof x; // string | number
x[0] = 2; // ok - number is a string-like index
const s = Symbol("s");
x[s] = "2"; // error: cannot access via symbol

interface N {
  n: number;
}

function fn(p: N) {
  return p.n;
}

const p1 = {
  n: 1
};

const p2 = {
  n: 2,
  s: "2"
};

fn(p1); // ok - exact match
fn(p2); // ok - structural matching: { n: number } present;  additional props ignored
fn({ n: 0, s: "s" }); // error: additional props not ignore when called explictily
fn({}); // error: n is missing

编辑

对象文字-显式创建某些形状的对象,如const p: { a: number} = { a: 42 },由类型记录以一种特殊的方式处理。与常规结构推断相反的是,这种类型必须完全匹配。老实说,这是有道理的,因为那些额外的财产--没有额外的可能是不安全的--无论如何都是无法获得的。

..。然而,TypeScript的立场是,这段代码中可能有一个bug。当将对象文本赋值给其他变量或将它们作为参数传递时,对象文本会得到特殊处理,并进行过多的属性检查。如果对象文字具有“目标类型”不具有的任何属性,您将得到一个错误。..。绕过这些检查的最后一种方法可能有点令人惊讶,就是将对象赋值给另一个变量。

TS手册

避免这个错误的另一个选择是.与{ [prop: string]: any }相交。

更多代码:

代码语言:javascript
复制
function f(x: { a: number }) {}
function g(y: { b: number }) {}
function h(z: { a: number } & { b: number }) {}

f({ a: 42, b: 58 } as { a: number }); // compiles - cast possible, but `b` inaccessible anyway
g({ a: 42 } as { b: number }); // does not compile - incorrect cast; Conversion of type '{ a: number; }' to type '{ b: number; }' may be a mistake
h({ a: 42, b: 58 }); // compiles!

const p = {
  a: 42,
  b: 58
};

f(p); // compiles - regular structural typing
g(p); // compiles - regular structural typing
h(p); // compiles - regular structural typing

const i: { a: number } = { a: 42, b: 58 }; // error: not exact match
f(i); // compiles
g(i); // error
h(i); // error
票数 3
EN

Stack Overflow用户

发布于 2020-11-30 11:05:20

这是一种关于交叉算子的推理方法。也许这会有帮助:

代码语言:javascript
复制
type Intersection = { a: string } & { b: number }

您可以将Intersection理解为“具有string 类型的属性anumber类型的属性b的对象”。这也恰好描述了这种简单的类型:

代码语言:javascript
复制
type Simple = { a: string; b: number }

这两种类型是兼容的。几乎所有的目的都可以用另一种代替。

我希望这解释了为什么HasKeyValue确实与您试图定义的类型相同。

至于为什么T_key_and_index不能工作,这是因为第一部分[a in k]: V定义了一个映射类型,并且在映射类型的定义中不能有额外的属性。如果需要向映射类型添加额外的属性,则可以创建&

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64969300

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档