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

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

搜索词

  • unique symbol
  • new unique symbol
  • new symbol

建议

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

用例

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

// ./es-globals/fundamentals.js
export const ESSymbol = Symbol;
export const { for: ESSymbol_for } = ESSymbol;
// ./es-globals/fundamentals.d.ts
export import ESSymbol = globalThis.Symbol;
export declare const ESSymbol_for: typeof ESSymbol.for;
// ./symbols.js
import { ESSymbol, ESSymbol_for } from "./es-globals/fundamentals.js";

// should be `unique symbol`, but is instead `symbol`:
export const customSymbol = ESSymbol("custom");

// should be `unique symbol` or `global symbol "nodejs.util.inspect.custom"`,
// but is instead `symbol`:
export const nodejs_util_inspect_custom = ESSymbol_for("nodejs.util.inspect.custom");

// should be `unique symbol` or `global symbol "nodejs.util.promisify.custom"`,
// but is instead `symbol`:
export const nodejs_util_promisify_custom = ESSymbol_for("nodejs.util.promisify.custom");

示例

这将允许将 SymbolConstructor 定义为:

declare interface SymbolConstructor {
	/**
* Returns a new unique Symbol value.
* @param description Description of the new symbol value.
*/
	(description?: string | number): new symbol;

	/**
* Returns a Symbol object from the global symbol registry matching the given key if found.
* Otherwise, returns a new symbol with this key.
* @param key key to search for.
*/
	for<T extends string>(key: T): global symbol T;
	// or, until GH-35909 is implemented:
	for(key: string): new symbol;
}

检查清单

我的建议满足以下准则:

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

另请参阅

6kkfgxo0

6kkfgxo01#

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

nhjlsmyf

nhjlsmyf2#

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

// What I originally expected to work
const [$arr, $head, $tail] = 'array head tail'.split(' ').map(Symbol.for);
// 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)
interface TestClass { [$arr]: boolean; } // using interface for this test because then I don't have to come up with new name for each example

// Can't cast to unique symbol. Err: 'symbol' expected.ts(1005)
const [$arrAofUS, $headAofUS, $tailAofUS] = 'array head tail'.split(' ').map(Symbol.for) as unique symbol[];
const [$arrCAofUS, $headCAofUS, $tailCAofUS] = 'array head tail'.split(' ').map(Symbol.for) as Array<unique symbol>;
// 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)
interface TestClass { [$arrAofUS]: boolean; [$arrCAofUS]: boolean; }

// None of these work either
const [$arrEAofS, $headEAofS, $tailEAofS] = 'array head tail'.split(' ').map(Symbol.for) as [symbol, symbol, symbol];
// 'unique symbol' types are not allowed here.ts(1335)
const [$arrEAofUS, $headEAofUS, $tailEAofUS] = 'array head tail'.split(' ').map(Symbol.for) as [unique symbol, unique symbol, unique symbol];
// 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)
interface TestClass { [$arrEAofS]: boolean; [$arrEAofUS]: boolean; }

// It's not the split messing it up.  Error: 'unique symbol' types are not allowed here.ts(1335)
const [$arrAM, $headAM, $tailAM] = ['array', 'head', 'tail'].map(Symbol.for);
// Doesn't work with destructuring!
const [$arrD, $headD, $tailD] = ([Symbol.for('array'), Symbol.for('head'), Symbol.for('tail')]);
// 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)
interface TestClass { [$arrAM]: boolean; [$arrD]: boolean; }

// Even when trying @Shou's hack
type UniqueSymbol = ReturnType<(a: string) => { readonly 0: unique symbol }[0]>;

// testing if UniqueSymbol works on the simple cases
// NOPE! Err: Type 'typeof $test' is not assignable to type 'typeof 0'.ts(2322)
const $testCustomUniqueSymbol: UniqueSymbol = Symbol.for('test');
// This does if it's casted before assignment
const $testCastedCustomUniqSym: UniqueSymbol = Symbol.for('test') as UniqueSymbol;
// but it's still not a unique symbol! Err: Type 'typeof 0' is not assignable to type 'typeof $testUniqSym'.ts(2322)
const $testNativeUniqSym: unique symbol = $testCustomUniqueSymbol;
// however it does work for regular symbol
const $testNativeSym: symbol = $testNativeUniqSym;

// Attempt at destructuring again, and even explicit casting
const [$arrFnUS, $headFnUS, $tailEFnUS]: [UniqueSymbol, UniqueSymbol, UniqueSymbol] = 'array head tail'.split(' ').map(Symbol.for) as [UniqueSymbol, UniqueSymbol, UniqueSymbol];
interface TestClass {
  [$testCustomUniqueSymbol]: boolean;   // Works so we know our UniqueSymbol type works for some cases
  [$testCastedCustomUniqSym]: boolean;  // Works
  [$testNativeUniqSym]: boolean;        // Works
  // Surprised testNativeSym doesn't work!
  [$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)
  [$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)
}

// Attempts to hack around it:
// Err: Type 'symbol[]' is not assignable to type '(unique symbol)[]'. Type 'symbol' is not assignable to type 'unique symbol'.ts(2322)
const toSymArr = (str: string): UniqueSymbol[] => str.split(' ').map(Symbol.for);
// hack around the error
const toSymArr2 = (str: string): UniqueSymbol[] => str.split(' ').map(Symbol.for) as any;
// will it work?
const [$arrFn, $headFn, $tailFn] = toSymArr('array head tail');
// 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)
interface TestClass { [$arrFn]: boolean;  }

wxclj1h5

wxclj1h53#

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

interface Entry<Value> {
    readonly id: unique symbol;
    value: Value;
}

class Collection<Item> {
    private entriesMap = new Map<symbol, Entry<Item>>();

    private createEntryID(): symbol {
        const id: unique symbol = Symbol('item');

        return id;
    }

    add(item: Item): void {
        const id = this.createEntryID();

        // `symbol`, cannot be casted to `unique symbol`
        const entry: Entry<Item> = {
            value: item,
            id,
//          ^ Error: Type 'symbol' is not assignable to type 'unique symbol'.
        };

        this.entriesMap.set(id, entry);
    }
}

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

interface Entry<Value> {
  readonly id: symbol;
  value: Value;
}

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

tquggr8v

tquggr8v4#

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

sgtfey8w

sgtfey8w5#

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

相关问题