TypeScript 允许函数具有新的符号作为返回类型,

snz8szmq  于 8个月前  发布在  TypeScript
关注(0)|答案(5)|浏览(92)

搜索词

  • unique symbol
  • new unique symbol
  • new symbol

建议

我请求将 unique symbol 作为函数声明的返回类型。
或者,使用 new symbol 可能会更好,以消除 #40106(注解)的歧义。

用例

目前,无法为全局 Symbol 构造函数创建别名或 Package 函数,并使用该构造函数构建唯一符号:

  1. // ./es-globals/fundamentals.js
  2. export const ESSymbol = Symbol;
  3. export const { for: ESSymbol_for } = ESSymbol;
  1. // ./es-globals/fundamentals.d.ts
  2. export import ESSymbol = globalThis.Symbol;
  3. export declare const ESSymbol_for: typeof ESSymbol.for;
  1. // ./symbols.js
  2. import { ESSymbol, ESSymbol_for } from "./es-globals/fundamentals.js";
  3. // should be `unique symbol`, but is instead `symbol`:
  4. export const customSymbol = ESSymbol("custom");
  5. // should be `unique symbol` or `global symbol "nodejs.util.inspect.custom"`,
  6. // but is instead `symbol`:
  7. export const nodejs_util_inspect_custom = ESSymbol_for("nodejs.util.inspect.custom");
  8. // should be `unique symbol` or `global symbol "nodejs.util.promisify.custom"`,
  9. // but is instead `symbol`:
  10. export const nodejs_util_promisify_custom = ESSymbol_for("nodejs.util.promisify.custom");

示例

这将允许将 SymbolConstructor 定义为:

  1. declare interface SymbolConstructor {
  2. /**
  3. * Returns a new unique Symbol value.
  4. * @param description Description of the new symbol value.
  5. */
  6. (description?: string | number): new symbol;
  7. /**
  8. * Returns a Symbol object from the global symbol registry matching the given key if found.
  9. * Otherwise, returns a new symbol with this key.
  10. * @param key key to search for.
  11. */
  12. for<T extends string>(key: T): global symbol T;
  13. // or, until GH-35909 is implemented:
  14. for(key: string): new symbol;
  15. }

检查清单

我的建议满足以下准则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性更改
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 这可以在不根据表达式的类型发出不同的 JS 的情况下实现
  • 这不是一个运行时特性(例如库功能、带有 JavaScript 输出的非 ECMAScript 语法等)
  • 这个特性会与 TypeScript's Design Goals 的其他部分保持一致。

另请参阅

6kkfgxo0

6kkfgxo01#

解:你可以通过一些类型层级的技巧来规避这个问题,但是我不知道$x_{as}$的类型转换是否作弊。

nhjlsmyf

nhjlsmyf2#

我尝试了一切,甚至尝试了@Shou的hack。没有绕过的办法,我们确实需要做一些改变。
我最大的问题是,当你解构一个唯一符号的数组时,TypeScript不再认为它们是唯一的。而且没有办法强迫TypeScript认为它们是唯一的。

  1. // What I originally expected to work
  2. const [$arr, $head, $tail] = 'array head tail'.split(' ').map(Symbol.for);
  3. // A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1166)
  4. interface TestClass { [$arr]: boolean; } // using interface for this test because then I don't have to come up with new name for each example
  5. // Can't cast to unique symbol. Err: 'symbol' expected.ts(1005)
  6. const [$arrAofUS, $headAofUS, $tailAofUS] = 'array head tail'.split(' ').map(Symbol.for) as unique symbol[];
  7. const [$arrCAofUS, $headCAofUS, $tailCAofUS] = 'array head tail'.split(' ').map(Symbol.for) as Array<unique symbol>;
  8. // A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
  9. interface TestClass { [$arrAofUS]: boolean; [$arrCAofUS]: boolean; }
  10. // None of these work either
  11. const [$arrEAofS, $headEAofS, $tailEAofS] = 'array head tail'.split(' ').map(Symbol.for) as [symbol, symbol, symbol];
  12. // 'unique symbol' types are not allowed here.ts(1335)
  13. const [$arrEAofUS, $headEAofUS, $tailEAofUS] = 'array head tail'.split(' ').map(Symbol.for) as [unique symbol, unique symbol, unique symbol];
  14. // A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
  15. interface TestClass { [$arrEAofS]: boolean; [$arrEAofUS]: boolean; }
  16. // It's not the split messing it up. Error: 'unique symbol' types are not allowed here.ts(1335)
  17. const [$arrAM, $headAM, $tailAM] = ['array', 'head', 'tail'].map(Symbol.for);
  18. // Doesn't work with destructuring!
  19. const [$arrD, $headD, $tailD] = ([Symbol.for('array'), Symbol.for('head'), Symbol.for('tail')]);
  20. // A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
  21. interface TestClass { [$arrAM]: boolean; [$arrD]: boolean; }
  22. // Even when trying @Shou's hack
  23. type UniqueSymbol = ReturnType<(a: string) => { readonly 0: unique symbol }[0]>;
  24. // testing if UniqueSymbol works on the simple cases
  25. // NOPE! Err: Type 'typeof $test' is not assignable to type 'typeof 0'.ts(2322)
  26. const $testCustomUniqueSymbol: UniqueSymbol = Symbol.for('test');
  27. // This does if it's casted before assignment
  28. const $testCastedCustomUniqSym: UniqueSymbol = Symbol.for('test') as UniqueSymbol;
  29. // but it's still not a unique symbol! Err: Type 'typeof 0' is not assignable to type 'typeof $testUniqSym'.ts(2322)
  30. const $testNativeUniqSym: unique symbol = $testCustomUniqueSymbol;
  31. // however it does work for regular symbol
  32. const $testNativeSym: symbol = $testNativeUniqSym;
  33. // Attempt at destructuring again, and even explicit casting
  34. const [$arrFnUS, $headFnUS, $tailEFnUS]: [UniqueSymbol, UniqueSymbol, UniqueSymbol] = 'array head tail'.split(' ').map(Symbol.for) as [UniqueSymbol, UniqueSymbol, UniqueSymbol];
  35. interface TestClass {
  36. [$testCustomUniqueSymbol]: boolean; // Works so we know our UniqueSymbol type works for some cases
  37. [$testCastedCustomUniqSym]: boolean; // Works
  38. [$testNativeUniqSym]: boolean; // Works
  39. // Surprised testNativeSym doesn't work!
  40. [$testNativeSym]: boolean; // Err: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
  41. [$arrFnUS]: boolean; // Err: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
  42. }
  43. // Attempts to hack around it:
  44. // Err: Type 'symbol[]' is not assignable to type '(unique symbol)[]'. Type 'symbol' is not assignable to type 'unique symbol'.ts(2322)
  45. const toSymArr = (str: string): UniqueSymbol[] => str.split(' ').map(Symbol.for);
  46. // hack around the error
  47. const toSymArr2 = (str: string): UniqueSymbol[] => str.split(' ').map(Symbol.for) as any;
  48. // will it work?
  49. const [$arrFn, $headFn, $tailFn] = toSymArr('array head tail');
  50. // nope! Err: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
  51. interface TestClass { [$arrFn]: boolean; }

展开查看全部
wxclj1h5

wxclj1h53#

我需要这个功能来定义一个集合类的方法,为它的每个项目创建唯一的令牌:

  1. interface Entry<Value> {
  2. readonly id: unique symbol;
  3. value: Value;
  4. }
  5. class Collection<Item> {
  6. private entriesMap = new Map<symbol, Entry<Item>>();
  7. private createEntryID(): symbol {
  8. const id: unique symbol = Symbol('item');
  9. return id;
  10. }
  11. add(item: Item): void {
  12. const id = this.createEntryID();
  13. // `symbol`, cannot be casted to `unique symbol`
  14. const entry: Entry<Item> = {
  15. value: item,
  16. id,
  17. // ^ Error: Type 'symbol' is not assignable to type 'unique symbol'.
  18. };
  19. this.entriesMap.set(id, entry);
  20. }
  21. }

到目前为止,我只能通过放弃 unique 的要求来解决这个问题:

  1. interface Entry<Value> {
  2. readonly id: symbol;
  3. value: Value;
  4. }

...而且,由于我不太理解 unique symbol ,所以我会采用这种方法。
开始一个新的“你需要多少反馈”系列的赛季,@RyanCavanaugh

展开查看全部
tquggr8v

tquggr8v4#

相对于这里的成本,相当多🙃。两年内15个赞和3条评论并不算多。

sgtfey8w

sgtfey8w5#

这个bug影响了Transcend Consent系统中的一个系统,该系统用于安全地向不受信任的第三方脚本暴露类API的子集。我们有一个名为createInterfaceViewerKeypair的函数,它返回两个unique symbol/new symbol,目前在TypeScript中无法正确输入。我们通过将其转换为unknown来解决这个问题,但这并不是理想的解决方案。

相关问题