【问题标题】:Using symbol as object-key type in TypeScript在 TypeScript 中使用符号作为对象键类型
【发布时间】:2020-03-25 20:19:50
【问题描述】:

我正在尝试使用 symbol 定义一个对象作为键类型,因为 MDN 说:

符号值可以用作对象属性的标识符 [...]

但使用它作为键属性的类型:

type obj = {
    [key: symbol | string]: string
}

导致以下错误:

TS1023:索引签名参数类型必须是“字符串”或“数字”。

即使它可以用作索引类型。 我正在使用最新的打字稿版本(v3.7.2),我发现的相关问题:

我还查看了typescript symbol docs,但它们只显示了它如何用作值,而不是类型。

例子:

const obj = {} as {
    [key: number | symbol]: string // Won't work
};

const sym = Symbol('My symbol');
obj[sym] = 'Hi';

Issue on Microsoft/TypeScript

Open feature request

【问题讨论】:

  • 我认为 TypeScript 只支持其对象类型声明中的特定符号。你真的想要any symbol吗?也许展示一个你想如何使用type obj 的例子 - 我怀疑所有符号键属性都是strings。
  • @Bergi 我添加了一个示例,也许我已经监督了一些事情,但我找不到让 ts 接受符号的方法(不使用 any 这是不好的做法)。
  • 不确定是否正确,但您是否尝试使用 Map<Symbol,String>,因为我们有 Map ,如果这可以达到您想要达到的目的
  • 对我来说同样的问题,我想烦人的部分是“TS 是 JS 的超集”的虚假广告——嗯,不完全是。这是一个完美的例子。

标签: javascript typescript types symbols


【解决方案1】:

不幸的是,目前这在 TypeScript 中是不可能的。如果您必须与一些期望这样做的 API 进行互操作,或者真的想要使用符号作为键,则可以使用这个尴尬的版本:

// Ensure we can not pass regular map to our custom functions
type SymbolMapTag = { readonly symbol: unique symbol }

type SymbolMap = SymbolMapTag & {
    [Key in string | number | symbol]: string;
}

function set_symbol<T extends SymbolMap, TSym extends symbol>
(target: T, sym: TSym, value: T[TSym]) {
    target[sym] = value;
}

function get_symbol<T extends SymbolMap, TSym extends symbol>
(target: T, sym: TSym): T[TSym] {
    return target[sym];
}

const symbol_map = {} as SymbolMap;

const sym = Symbol('My symbol');
set_symbol(symbol_map, sym, "hi");
get_symbol(symbol_map, sym); // string


type NonSymbolMap = {
    [Key in string | number]: string;
}

const non_symbol_map = {} as NonSymbolMap;
set_symbol(non_symbol_map, sym, "hi"); // error
get_symbol(non_symbol_map, sym); // error

【讨论】:

    【解决方案2】:

    TypeScript 4.4 允许symbols 进入index signatures

    type SymbolIndex = {
        [key: symbol | string]: string // works
    }
    
    const sym = Symbol("descr");
    const t1: SymbolIndex = {
        "foo": "bar",
        [Symbol.iterator]: "qux",
        sym: "sym"
    };
    
    // all result in string
    t1.foo 
    t1.sym 
    t1[Symbol.iterator]
    t1["oh"]
    

    Playground

    对于旧版本,SymbolIndex 将触发 error

    索引签名参数类型必须是“字符串”或“数字”。(1023)

    另类

    如果您只想要一个带有符号且没有索引签名的对象类型,那么您今天已经可以做到了:

    const sym = Symbol() // note const (no let) 
    type O = {
        foo: string
        [Symbol.iterator]: string
        [sym]: number
    }
    
    let o: O = { [sym] : 3, [Symbol.iterator]: "bar", foo: "qux"}
    
    let { [sym]: symVal } = o
    

    Playground

    【讨论】:

    • 如果我们有两个Symbol(),那么我们如何将[Symbol.iterator] 用于两个不同的符号
    • @SubratoPatnaik 老实说,我不明白你的问题。 Symbol.iterator 是一个众所周知的符号,可以为每个对象添加一次作为属性键以提供a default iterator,可与for...of 一起使用。如果您指的是类型空间还是值空间,也不清楚。
    • 我可以在操场上看到代码,它运行没有任何问题。但是,在我使用 TypeScript 4.3 将它们复制到我的 vscode 之后,它仍然告诉我 An index signature parameter type must be either 'string' or 'number'.ts(1023)
    • @Huan 该功能已延迟到即将推出的 TS 4.4,更新答案
    • 谢谢,@ford04!我已经更新到 TypeScript 4.4-dev,现在一切正常。
    猜你喜欢
    • 2019-10-12
    • 2019-02-03
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多