类属性上的TypeScript联合类型未按预期工作

cuxqih21  于 2022-12-24  发布在  TypeScript
关注(0)|答案(1)|浏览(120)

我试图为我们的模型类正确地输入一个属性 Package 器,然后这个属性会得到一个代理setter,这个代理setter被假定为接受原始的、解析过的值和 Package 器示例本身。代码本身已经工作了,只是输入引起了问题。这个问题最好用代码来解释。

Playground链接
**属性 Package **

abstract class BaseProp {}

type RawType = `/${string}/${number}` | null;
type ValType<M> = M | null | undefined;

type ResolvableProxy<M> = NonNullable<Resolvable<M> | RawType | ValType<M>>
class Resolvable<M> extends BaseProp {
    raw!: RawType;
    val!: ValType<M>;

    // has internal logic to resolve how to assign which is called via a proxy
    constructor (model: M) { super(); }

    resolve () {  return this; }
}

型号定义

class ModelA {
    name!: string;
}
class ModelB {
    a: ResolvableProxy<ModelA> = new Resolvable(ModelA);

    // the proxy handles properties that are extended from BaseProp
    // constructor () { return new Proxy(); }
}

模型被 Package 到一个代理中,它对扩展BaseProp的所有内容都有特殊的处理,我没有包含这部分代码,因为我认为它在这里不相关。代理按预期工作,只是类型问题。

测试用途

let test1: ResolvableProxy<ModelA> = new Resolvable(ModelA);
test1.resolve(); // accessable
const test1Name = test1.val!.name; // accessable
test1.raw = '/path/1'; // accessable
test1.val = new ModelA(); // accessable
test1 = '/path/1';
test1 = new ModelA();
test1 = new Resolvable(ModelA);

预期用途

当用于类属性时,union类型的行为不同,这对我来说没有意义。

let test2 = new ModelB();
test2.a.resolve(); // not accessable, why?
test2.a.raw = '/path/1'; // not accessable, why?
test2.a.val = new ModelA(); // not accessable, why?
test2.a = '/path/1';
test2.a = new ModelA();
test2.a = new Resolvable(ModelA);

使用“as”允许访问Resolvable成员,但这将需要重写大量代码,这应该是不必要的,因为类型本身应该是正确的IMO。当循环类似数组的结构时,它也会变得过于复杂。

(test2.a as Resolvable<ModelA>).raw = '/path/1';
const test3 = (test2.a as Resolvable<ModelA>).val;
(test2.a as Resolvable<ModelA>).resolve();

我曾想过对每个属性使用显式getter/setter,但这会带来其他问题,比如“a”不是JSON字符串化格式,至少没有额外的处理。

export class ModelC  {
    // @ts-ignore
    #a: Resolvable<ModelA> = new Resolvable(ModelA);
    get a(): Resolvable<ModelA> { return this.#a; }

    set a(v: ResolvableProxy<ModelA>) { this.#a = v as any; }
}

const test4 = new ModelC();
test4.a.resolve();
test4.a.raw = '/path/1';
test4.a.val = new ModelA();
test4.a = '/path/1';
test4.a = new ModelA();
test4.a = new Resolvable(ModelA);
ehxuflar

ehxuflar1#

它在第一次测试中不会出错,因为它的类型被typechecker简化了
如果你打破了代码块,它会出错

function testUsage() {
    let test1: ResolvableProxy<ModelA> = new Resolvable(ModelA);
    function f() {
        test1.resolve(); // error
        const test1Name = test1.val!.name; // error
        test1.raw = '/path/1'; // error
        test1.val = new ModelA(); // error
        test1 = '/path/1';
        test1 = new ModelA();
        test1 = new Resolvable(ModelA);
    }
}

相关问题