【问题标题】:TypeScript Constructor Overload with Empty Constructor带有空构造函数的 TypeScript 构造函数重载
【发布时间】:2019-03-27 08:12:55
【问题描述】:

为什么在 TypeScript 中不允许有单独的 constructor 定义?
拥有例如两个constructors,我需要这样写我的代码。

constructor(id: number)
constructor(id: number, name?: string, surname?: string, email?: string) {
    this.id = id;
    this.name = name;
    this.surname = surname;
    this.email = email;
}

因此,我需要将? 放在第一个构造函数中不需要的参数之后。

为什么我不能这样写?

constructor(id: number) {
    this.id = id;
}

constructor(id: number, name: string, surname: string, email: string) {
    this.id = id;
    this.name = name;
    this.surname = surname;
    this.email = email;
}

因此对于两个构造函数来说,所有参数都是强制性的。

此外,如果我需要一个空的构造函数,事情会变得更奇怪,因为我需要用? 标记每个参数。

constructor()
constructor(id?: number, name?: string, surname?: string, email?: string) {
    this.id = id;
    this.name = name;
    this.surname = surname;
    this.email = email;
}

为什么 TypeScriptC#Python 等常用语言在这里有所不同?

我希望它能像这样工作。

constructor() {

}
constructor(id: number, name: string, surname: string, email: string) {
    this.id = id;
    this.name = name;
    this.surname = surname;
    this.email = email;
}

所以你可以不传参数或者必须传所有参数。

【问题讨论】:

  • 其实我根本不需要constructor(),如果所有参数都标有?
  • 只是让您知道签名()+(id?) 不会暴露id 签名。你需要()+(id)+(id?)。详情见我的回答
  • 也不是每个公共重载都需要是可选的。所以你的公共界面可以是安全的。再次,请参阅我的答案。

标签: constructor typescript


【解决方案1】:

因为您的构造函数实现被所有重载构造函数调用。 (从技术上讲,在运行时只有一个构造函数被各种重载参数签名调用。)

想象一下:

overload_constructor(id:string) {
    implementation_constructor(id);
}

implementation_constructor(id:string, name?:string, age?:number) {
    // ...
}

这样想,overload_constructor 不能调用implementation_constructor,除非nameage 是可选的。

另请参阅 Basarat 的回答,类型检查器不会公开该实现以供公众使用(尽管在运行时它是 JS 中使用的“真实”构造函数)。如果您只想允许 ()(id)(id, name, surname, email) 作为唯一有效的调用签名,您可以这样做:

constructor()
constructor(id: number)
constructor(id: number, name: string, surname: string, email: string)
constructor(id?: number, name?: string, surname?: string, email?: string) {
    this.id = id;
    this.name = name;
    this.surname = surname;
    this.email = email;
}

请注意,在实现中所有参数都是可选的,但编译时不会公开签名,您只能使用这些调用:

new Foo()
new Foo(1)
new Foo(1, "a", "b", "c")

不是,例如:

new Foo(1, "a")

【讨论】:

  • 这是有道理的。你知道这是对 TypeScript 转换的 JavaScript (ES5) 的限制,还是 TypeScript 开发人员的设计决定?
  • 嗯,ES5 根本没有类,只有构造函数。您不能让多个构造函数代表一个对象类型。在 ES6 中,我们有类,但只有一个构造函数。在 JS 中,所有函数参数始终都是可选的,并且没有类型。这就是我们使用 TypeScript 的原因。 :-)
  • 那么为什么 TypeScript 中只有一个构造函数(在运行时)?我没有得到好处:D
  • TypeScript 在运行时只有一个构造函数,因为它在运行时被转译为 JS,而 JS 无论如何都不支持多个构造函数。构造函数重载仅用于 TypeScript 编译器,以实现更好的静态类型。
【解决方案2】:

最后一个函数重载只在实现中使用,不公开。如下图所示:

class Foo{
    constructor()
    constructor(id?: number) {
    }
}

const foo1 = new Foo();
const foo2 = new Foo(123); // Error! : not public

如果您希望 id:number 公开可用,当然您可以添加另一个重载:

class Foo{
    constructor()
    constructor(id: number)
    constructor(id?: number) {
    }
}

const foo1 = new Foo();
const foo2 = new Foo(123); // Okay
const foo3 = new Foo('hello'); // Error: Does not match any public overload

原因是 TypeScript 尽量不为函数重载进行花哨的代码生成(传统语言使用名称修饰来实现这一点,例如 C++)

所以你可以不传参数也可以传参数。

实际上,您可以将最终重载设为可选,但公共重载都不是可选的。考虑以下示例:

class Foo{  
    constructor(id: number, name:string)
    constructor(name:string)
    constructor(idOrName?: number|string, name?:string) {
    }
}

const foo1 = new Foo('name'); // Okay
const foo2 = new Foo(123); // Error: you must provide a name if you use the id overload
const foo3 = new Foo(123,'name'); // Okay

【讨论】:

    【解决方案3】:

    您可以使用 Builder 模式来解决这个问题。即使在 C# 或 Python 中,随着构造函数参数数量的增加,它也很快成为更好的方法。

    class Foo {
      constructor(public id: number, public name: string, public surname: string, public email: string) {
      }
      static Builder = class {
        id: number = NaN;
        name: string = null;
        surname: string = null;
        email: string = null;
        Builder() {
        }
        build(): Foo {
          return new Foo(this.id, this.name, this.surname, this.email);
        }
      }
    }
    

    【讨论】:

      【解决方案4】:

      如果您使用静态方法来实现重载构造函数,请参阅。

      export class User implements IUser {
           constructor(
              private _id: string,
              private _name: string,
              private _email: string,
            ) {}
          static New(jsonUser:string){
              return new User(
                  JSON.parse(jsonUser).id,
                  JSON.parse(jsonUser).name,
                  JSON.parse(jsonUser).email)
          }
      }
      

      【讨论】:

      • 你能重载静态方法吗?
      猜你喜欢
      • 2012-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-03
      • 1970-01-01
      • 2013-02-26
      • 1970-01-01
      相关资源
      最近更新 更多