なぜconstructor
をTypeScriptで個別に定義できないのですか?
two 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;
}
ここでTypeScriptがC#
やPython
などの一般的な言語と異なるのはなぜですか?
私はそれがこのように機能することを期待しています。
constructor() {
}
constructor(id: number, name: string, surname: string, email: string) {
this.id = id;
this.name = name;
this.surname = surname;
this.email = email;
}
そのため、パラメータなしで渡すことも、すべてのパラメータを渡す必要があります。
コンストラクターの実装は、すべてのオーバーロードコンストラクターによって呼び出されるためです。 (技術的には、実行時には、さまざまなオーバーロード引数シグネチャで呼び出されるコンストラクター関数が1つだけあります。)
次のように想像してください:
overload_constructor(id:string) {
implementation_constructor(id);
}
implementation_constructor(id:string, name?:string, age?:number) {
// ...
}
このように考えると、overload_constructor
は、name
およびage
がオプションでない限り、implementation_constructor
を呼び出すことができませんでした。
また、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")
最後の関数オーバーロードは実装でのみ使用され、公開されていません。これを以下に示します。
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
これを解決するには、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);
}
}
}