如何在'k'和number上使用'k'和抽象类型的number?我希望有一个类型别名T,这样T<'k', number>就会给出该类型。
请考虑以下示例:
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中组合时,我会得到一个解析错误(甚至不是语义错误,它甚至看起来都不是有效的语法)。这在这里似乎是可行的:
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }但在这里,我不太明白为什么它不抱怨额外的属性( &右侧的类型不应该允许具有额外属性的对象)。
编辑
在回答中曾多次提到&是相交算子,它的行为应该与集合论交点相似。然而,在处理额外属性时,情况并非如此,下面的示例说明了这一点:
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 }“相交”使类型“更大”而不是使其更小,我将不胜感激。
我见过这些问题
但这些似乎没有直接的关系,尽管有一个相似的名字。
发布于 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排除为索引。
我认为您的困惑可能来自于类型记录如何对待函数参数。使用显式定义的参数调用函数与将参数作为变量传递不同。在这两种情况下,类型记录确保所有参数都是正确的结构/形状,但在后一种情况下,它另外不允许额外的道具。
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。当将对象文本赋值给其他变量或将它们作为参数传递时,对象文本会得到特殊处理,并进行过多的属性检查。如果对象文字具有“目标类型”不具有的任何属性,您将得到一个错误。..。绕过这些检查的最后一种方法可能有点令人惊讶,就是将对象赋值给另一个变量。
避免这个错误的另一个选择是.与{ [prop: string]: any }相交。
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发布于 2020-11-30 11:05:20
这是一种关于交叉算子的推理方法。也许这会有帮助:
type Intersection = { a: string } & { b: number }您可以将Intersection理解为“具有string 类型的属性a和number类型的属性b的对象”。这也恰好描述了这种简单的类型:
type Simple = { a: string; b: number }这两种类型是兼容的。几乎所有的目的都可以用另一种代替。
我希望这解释了为什么HasKeyValue确实与您试图定义的类型相同。
至于为什么T_key_and_index不能工作,这是因为第一部分[a in k]: V定义了一个映射类型,并且在映射类型的定义中不能有额外的属性。如果需要向映射类型添加额外的属性,则可以创建&。
https://stackoverflow.com/questions/64969300
复制相似问题