Typescript: 为类创建“copy”构造函数时出现类型错误

huangapple go评论87阅读模式
英文:

Typescript: type error emerged on creating a "copy" constructor for class

问题

I would like to add a constructor to MyClass which takes 2 parameters, first one is instance of MyClass and second one is partial of MyClass non-function properties. Example is I have below MyClass

我想在MyClass中添加一个构造函数,该构造函数接受2个参数,第一个是MyClass的实例,第二个是MyClass的非函数属性的部分。示例是我有以下的MyClass:

class MyClass {
public variable1: string | undefined;
public variable2: number | undefined;

public function1(): void {
    console.log("log from function1.");
}

constructor(variable1: string, variable2: number) {
    this.variable1 = p1;
    this.variable2 = p2;
}

}

and

const instance1 = new MyClass("a", 1);
const instance2 = new MyClass(instance1, { variable1: "b" }); <= now instance2 is instance with variable1 as "b" and variable2 remains 1

以及

const instance1 = new MyClass("a", 1);
const instance2 = new MyClass(instance1, { variable1: "b" }); <= 现在instance2是一个具有variable1为"b"和variable2保持为1的实例

I changed MyClass as below

我将MyClass更改如下:

type PickByValue<T, ValueType> = Pick<T, {
[Key in keyof T]-?: T[Key] extends ValueType ? never : Key;
}[keyof T]>;

type NonFunction = PickByValue<MyClass, Function>;
type UUKeys = keyof NonFunction;

class MyClass {
public variable1: string | undefined;
public variable2: number | undefined;

public function1(): void {
    console.log(this.variable1 ?? "variable missing " + this.variable2?.toString() ?? "variable2 missing");
}

// First constructor
constructor(variable1: string, variable2: number);
// Second constructor
constructor(p1: MyClass, p2: Partial<NonFunction>);
// Implementation
constructor(p1: MyClass | string, p2?: Partial<NonFunction> | number) {
    if (typeof p1 === 'string' && typeof p2 === 'number') {
        this.variable1 = p1;
        this.variable2 = p2;
    } else if (p1 instanceof MyClass && p2 && typeof p2 !== "number" && p2 satisfies Partial<NonFunction>) {
        for (const key of Object.keys(p1)) {
            if (typeof p1[key as keyof MyClass] !== "function") {
                const k = key as UUKeys;
                this[k] = p2[k] ?? p1[k];
                ^^^^^^  <= ERROR: Type 'string | number | undefined' is not assignable to type 'undefined'. Type 'string' is not assignable to type 'undefined'.
            }
        }
    } else {
        this.variable1 = '';
        this.variable2 = 0;
    }
}

}

Error emerged confused my which I could not understand where undefined coming from as I already exclude undefined by converting key to UUKeys.

错误出现混淆了我,我无法理解undefined来自哪里,因为我已经通过将key转换为UUKeys来排除了undefined

Type 'string | number | undefined' is not assignable to type
'undefined'. Type 'string' is not assignable to type 'undefined'.

类型'string | number | undefined'不能赋值给类型'undefined'。类型'string'不能赋值给类型'undefined'。

BTW, I am using Typescript v5.1.6.

顺便说一下,我正在使用Typescript v5.1.6。

英文:

I would like to add a constructor to MyClass which takes 2 parameters, first one is instance of MyClass and second one is partial of MyClass non-function properties. Example is I have below MyClass

class MyClass {
    public variable1: string | undefined;
    public variable2: number | undefined;

    public function1(): void {
        console.log(&quot;log from function1.&quot;);
    }

    constructor(variable1: string, variable2: number) {
        this.variable1 = p1;
        this.variable2 = p2;
    }
}

and

const instance1 = new MyClass(&quot;a&quot;, 1);
const instance2 = new MyClass(instance1, { variable1: &quot;b&quot; });  &lt;= now instance2 is instance with variable1 as &quot;b&quot; and variable2 remains 1

I changed MyClass as below

type PickByValue&lt;T, ValueType&gt; = Pick&lt;T, {
    [Key in keyof T]-?: T[Key] extends ValueType ? never : Key;
}[keyof T]&gt;;

type NonFunction = PickByValue&lt;MyClass, Function&gt;;
type UUKeys = keyof NonFunction;

class MyClass {
    public variable1: string | undefined;
    public variable2: number | undefined;

    public function1(): void {
        console.log(this.variable1 ?? &quot;variable missing &quot; + this.variable2?.toString() ?? &quot;variable2 missing&quot;);
    }

    // First constructor
    constructor(variable1: string, variable2: number);
    // Second constructor
    constructor(p1: MyClass, p2: Partial&lt;NonFunction&gt;);
    // Implementation
    constructor(p1: MyClass | string, p2?: Partial&lt;NonFunction&gt; | number) {
        if (typeof p1 === &#39;string&#39; &amp;&amp; typeof p2 === &#39;number&#39;) {
            this.variable1 = p1;
            this.variable2 = p2;
        } else if (p1 instanceof MyClass &amp;&amp; p2 &amp;&amp; typeof p2 !== &quot;number&quot; &amp;&amp; p2 satisfies Partial&lt;NonFunction&gt;) {
            for (const key of Object.keys(p1)) {
                if (typeof p1[key as keyof MyClass] !== &quot;function&quot;) {
                    const k = key as UUKeys;
                    this[k] = p2[k] ?? p1[k];
                    ^^^^^^  &lt;= ERROR: Type &#39;string | number | undefined&#39; is not assignable to type &#39;undefined&#39;. Type &#39;string&#39; is not assignable to type &#39;undefined&#39;.
                }
            }
        } else {
            this.variable1 = &#39;&#39;;
            this.variable2 = 0;
        }
    }
}

Error emerged confused my which I could not understand where undefined coming from as I already exclude undefined by converting key to UUKeys.

> Type 'string | number | undefined' is not assignable to type
> 'undefined'. Type 'string' is not assignable to type 'undefined'

BTW, I am using Typescript v5.1.6.

答案1

得分: 1

只返回翻译好的部分:

这会更有意义,如果我们看一下键 k 的类型和值 v

if (typeof p1[key as keyof MyClass] !== 'function') {
  const k = key as UUKeys;  // 'variable1' | 'variable2'
  const v = p2[k] ?? p1[k]; // number | string | undefined
  this[k] = v;
}
  • 键的类型是该类的属性的枚举(不是函数)。
  • 值的类型是该类的所有(非函数)属性的类型的联合。在这种情况下,该值可以是数字、字符串或未定义。

如果键是 variable1,并且值是数字,则它将无效。如果键是 variable2,并且值是字符串,它也将无效。所有属性的唯一有效值是 undefined


在这种情况下,您已经确定值具有正确的键类型,因为 p2MyClass 的部分。

这是一个典型的情况,称为“我知道我在做什么”。减少类型检查。

(this as any)[key] = p2[key] ?? p1[key];
英文:

It makes more sense if we look at the type of the key k and the value v.

if (typeof p1[key as keyof MyClass] !== &#39;function&#39;) {
  const k = key as UUKeys;  // &#39;variable1&#39; | &#39;variable2&#39;
  const v = p2[k] ?? p1[k]; // number | string | undefined
  this[k] = v;
}
  • The key type is an enum of the properties of the class (that's not a function).
  • The value type is a union of the types of all (non-function) properties of the class. In this case, the value can either be a number, string, or undefined.

If the key is variable1 and the value is a number, it would be invalid. If the key is variable2 and the value is a string, it would also be invalid. The only valid value for all properties is undefined.


In this case, you're already sure that the value has the correct type for the key, because p2 is a partial of MyClass.

This is a typical case of "I know what I'm doing". Ease down on the type-checking.

(this as any)[key] = p2[key] ?? p1[key];

huangapple
  • 本文由 发表于 2023年7月14日 08:30:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76684021.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定