请看docs
为了将符号视为唯一文字,可以使用新类型的唯一符号。唯一符号是符号的子类型,仅通过调用 Symbol() 或 Symbol.for() 或显式类型注释产生。新类型仅允许在 const 声明和只读静态属性 and in order to reference a specific unique symbol, you’ll have to use the typeof operator 上使用。每个对唯一符号的引用都意味着与给定声明相关联的完全唯一身份。
特别是这个:in order to reference a specific unique symbol, you’ll have to use the typeof operator
因此,要使用unique symbol 约束,您需要使用typeof:
const UNIQUE: unique symbol = Symbol();
type MARKED<U extends typeof UNIQUE> = U
type Result = MARKED<typeof UNIQUE>
但是,我敢打赌,您有多个 unique symbols,并且您不想为每个符号创建实用程序类型。
让我们仔细看看这条线:unique symbol is a subtype of symbol。
它给了我们什么?
unique symbol 是符号的子类型。它为我们提供了检查某种类型是否是符号子类型的门。已经是东西了。
所以,我们需要检查传递的类型是否是symbol的子类型:
type IsUnique<UniqueSymbol extends symbol> =
(UniqueSymbol extends symbol
? (symbol extends UniqueSymbol
? false
: true
)
: false
)
// true
type Check = IsUnique<symbol & { tag: 42 }>
上面的 util 不符合我们的要求。
我们还需要检查符号是否只包含symbol 键。
type IsExactKeys<S extends symbol> =
(keyof S extends SYMBOL_KEYS
? (SYMBOL_KEYS extends keyof S
? true
: false
)
: false
)
type IsUnique<UniqueSymbol extends symbol> =
(UniqueSymbol extends symbol
? (symbol extends UniqueSymbol
? false
: IsExactKeys<UniqueSymbol>
)
: false
)
// never
type Check = IsUnique<never>
糟糕,它仍然无法与never 一起使用。
让我们处理never类型:
const UNIQUE: unique symbol = Symbol();
const REGULAR: symbol = Symbol()
type SYMBOL_KEYS = keyof symbol;
type IsExactKeys<S extends symbol> =
(keyof S extends SYMBOL_KEYS
? (SYMBOL_KEYS extends keyof S
? true
: false
)
: false
)
// credits goes to https://github.com/microsoft/TypeScript/issues/40248
type IsNever<T> = [T] extends [never] ? true : false;
type IsUnique<UniqueSymbol extends symbol> =
(IsNever<UniqueSymbol> extends true ? false
: (UniqueSymbol extends symbol
? (symbol extends UniqueSymbol
? false
: IsExactKeys<UniqueSymbol>
)
: false
)
);
type Assert<T extends true> = T
type Unique = {
readonly StaticSymbol: unique symbol
}['StaticSymbol']
/**
* Ok
*/
type Test0_0 = Assert<IsUnique<Unique>> // ok
type Test0 = Assert<IsUnique<typeof UNIQUE>> // ok
/**
* Expected errors
*/
type Test1 = Assert<IsUnique<typeof REGULAR>> // error
type Test2 = Assert<IsUnique<
& typeof REGULAR
& { readonly tag: unique symbol }
>> // error
type Test3 = Assert<IsUnique<symbol>> // error
type Test4 = Assert<IsUnique<any>> // error
type Test5 = Assert<IsUnique<unknown>> // error
type Test6 = Assert<IsUnique<never>> // error
export type MARKED<C, M extends symbol> =
IsUnique<M> extends true ? C & {
readonly m: M;
} : never
Playground
现在好像可以了。请记住,上述约束是间接的。我会说这是一个duck 约束。
如果有:
- 符号的子类型
- 具有与符号相同的键
- 不是永远
- 符号不可分配给传递的类型,但传递的类型可分配给符号
那么,我们可能与unique symbol 达成协议。
请添加更多测试。
请仅在 90% 的情况下信任上述代码。