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
a
const
”)显然会降低编译器的性能。代码总是索引到对象中,因此编译器为每次这样的访问所做的任何额外工作都会降低速度。它是否将性能降低得太多而不值得?不确定。这就是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
}