Typescript 为什么字符串和符号在歧视工会的问题上工作不一致?
为什么第二个实现在以下代码中发出错误Typescript 为什么字符串和符号在歧视工会的问题上工作不一致?,typescript,types,Typescript,Types,为什么第二个实现在以下代码中发出错误 type Op<T> = (value: T) => void // Usecase: we need to mix some Op<string> with some Op<ohter> in one array // implementation 1 type MixedOp<T> = | (Op<string> & { __isString: true }) //
type Op<T> = (value: T) => void
// Usecase: we need to mix some Op<string> with some Op<ohter> in one array
// implementation 1
type MixedOp<T> =
| (Op<string> & { __isString: true }) // __isString is used for runtime check
| (T extends string ? never : (Op<T> & { __isString: false }))
function run<T>(funcs: MixedOp<T>[], value: string, transform: (value: string) => T) {
for (let fun of funcs) {
if (fun.__isString) {
fun(value)
} else {
fun(transform(value))
}
}
}
// implementation 2
// the only difference from implementation 1 is replaced __isString with a symbol
// while that replacement leads to an error
const IsStringSymbol: unique symbol = Symbol()
type MixedOp2<T> =
| (Op<string> & { [IsStringSymbol]: true })
| (T extends string ? never : (Op<T> & { [IsStringSymbol]: false }))
function run2<T>(funcs: MixedOp2<T>[], value: string, transform: (value: string) => T) {
for (let fun of funcs) {
if (fun[IsStringSymbol]) {
fun(value) // Argument of type 'string' is not assignable to parameter of type 'string & T'.
} else {
fun(transform(value)) // Argument of type 'T' is not assignable to parameter of type 'string & T'.
}
}
}
type Op=(值:T)=>void
//用例:我们需要在一个数组中混合一些Op和一些Op
//实施1
类型MixedOp=
|(Op&{uuu-isString:true})/\uu-isString用于运行时检查
|(T扩展字符串?从不:(Op&{uu isString:false}))
函数运行(funcs:MixedOp[],value:string,transform:(value:string)=>T){
为了(让funcs变得有趣){
如果(有趣){
乐趣(价值)
}否则{
乐趣(转化(价值))
}
}
}
//实施2
//与实现1的唯一区别是用符号替换了isString
//而这种替换会导致错误
常量IsString符号:唯一符号=符号()
类型MixedOp2=
|(Op&{[IsStringSymbol]:true})
|(T扩展字符串?从不:(Op&{[IsStringSymbol]:false}))
函数run2(funcs:MixedOp2[],值:string,transform:(值:string)=>T){
为了(让funcs变得有趣){
如果(乐趣[IsString符号]){
fun(value)//不能将“string”类型的参数赋值给“string&T”类型的参数。
}否则{
fun(transform(value))//类型为“T”的参数不能分配给类型为“string&T”的参数。
}
}
}
这看起来像是当前的设计限制或TypeScript中缺少的功能。根据和,当前不能使用
符号
命名属性作为属性的判别式
如果我有一个常规的判别并集,其中判别键是字符串文字
const k = "a"
type U = { [k]: true, b: number } | { [k]: false, c: string };
declare const u: U;
然后我可以使用它来区分该并集类型的值,只要我通过文本键直接访问属性:
// direct access
if (u["a"]) { // you could also write u.a
u.b.toFixed(); // okay
} else {
u.c.toUpperCase(); // okay
}
如果我试图访问判别式,其中键是存储在变量中的const
,它会中断:
// via variable
if (u[k]) {
u[k].toFixed(); // error
} else {
u[k].toUpperCase(); // error
}
编译器不会尝试这样做,因为检查(“这是判别类型的k
aconst
”)显然会降低编译器的性能。代码总是索引到对象中,因此编译器为每次这样的访问所做的任何额外工作都会降低速度。它是否将性能降低得太多而不值得?不确定。这就是microsoft/TypeScript#36230的内容
如果您尝试在识别密钥是符号的情况下执行相同的操作,则会遇到问题,因为无法直接使用密钥: 这会导致无法使用
符号
命名属性来区分联合。这就是microsoft/TypeScript#36463的内容
那么,你能做什么?大概您仍然应该使用
\uu isString
字符串键继续。此外,你可以去,给它一个我不明白的问题标题如何适用;为什么你认为这与分布条件类型有关?在我看来,这更像是一个歧视工会的问题。你说得对,我道歉。我只是不太理解这些概念。我已经改变了标题。
const k = Symbol();
type U = { [k]: true, b: number } | { [k]: false, c: string };
declare const u: U;
/*
if (u[???]) { // what do I put in the ???
u.b.toFixed();
} else {
u.c.toUpperCase();
}
*/
if (u[k]) {
u[k].toFixed(); // error
} else {
u[k].toUpperCase(); // error
}