TypeScript 中的构造函数重载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12702548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Constructor overload in TypeScript
提问by Ted
Has anybody done constructor overloading in TypeScript. On page 64 of the language specification (v 0.8), there are statements describing constructor overloads, but there wasn't any sample code given.
有没有人在 TypeScript 中做过构造函数重载。在语言规范 (v 0.8) 的第 64 页上,有描述构造函数重载的语句,但没有给出任何示例代码。
I'm trying out a really basic class declaration right now; it looks like this,
我现在正在尝试一个非常基本的类声明;看起来像这样,
interface IBox {
x : number;
y : number;
height : number;
width : number;
}
class Box {
public x: number;
public y: number;
public height: number;
public width: number;
constructor(obj: IBox) {
this.x = obj.x;
this.y = obj.y;
this.height = obj.height;
this.width = obj.width;
}
constructor() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
}
}
When ran with tsc BoxSample.ts, it throws out a duplicate constructor definition -- which is obvious. Any help is appreciated.
当使用 tsc BoxSample.ts 运行时,它会抛出一个重复的构造函数定义——这很明显。任何帮助表示赞赏。
回答by chuckj
TypeScript allows you to declare overloads but you can only have one implementation and that implementation must have a signature that is compatible with all overloads. In your example, this can easily be done with an optional parameter as in,
TypeScript 允许您声明重载,但您只能有一个实现,并且该实现必须具有与所有重载兼容的签名。在您的示例中,这可以使用可选参数轻松完成,例如,
interface IBox {
x : number;
y : number;
height : number;
width : number;
}
class Box {
public x: number;
public y: number;
public height: number;
public width: number;
constructor(obj?: IBox) {
this.x = obj && obj.x || 0
this.y = obj && obj.y || 0
this.height = obj && obj.height || 0
this.width = obj && obj.width || 0;
}
}
or two overloads with a more general constructor as in,
或两个具有更通用构造函数的重载,例如,
interface IBox {
x : number;
y : number;
height : number;
width : number;
}
class Box {
public x: number;
public y: number;
public height: number;
public width: number;
constructor();
constructor(obj: IBox);
constructor(obj?: any) {
this.x = obj && obj.x || 0
this.y = obj && obj.y || 0
this.height = obj && obj.height || 0
this.width = obj && obj.width || 0;
}
}
回答by 2grit
Regarding constructor overloadsone good alternative would be to implement the additional overloads as static factory methods. I think its more readable and simple than checking for all possible argument combinations at the constructor. Here is a simple example:
关于构造函数重载,一个不错的选择是将额外的重载实现为静态工厂方法。我认为它比在构造函数中检查所有可能的参数组合更具可读性和简单性。这是一个简单的例子:
function calculateAge(birthday): number {
return new Date().getFullYear() - birthday;
}
class Person {
static fromData(data: PersonData): Person {
const { first, last, birthday, gender = 'M' } = data;
return new this(
`${last}, ${first}`,
calculateAge(birthday),
gender,
);
}
constructor(
public fullName: string,
public age: number,
public gender: 'M' | 'F',
) {}
toString(): string {
return `Hello, my name is ${this.fullName} and I'm a ${this.age}yo ${this.gender}`;
}
}
interface PersonData {
first: string;
last: string;
birthday: string;
gender?: 'M' | 'F';
}
const personA = new Person('Doe, John', 31, 'M');
console.log(personA.toString());
const personB = Person.fromData({
first: 'Jane',
last: 'Smith',
birthday: '1986',
gender: 'F',
});
console.log(personB.toString());
Method overloading in TypeScript isn't for real, let's say, as it would require too much compiler-generated code and the core team try to avoid that at all costs. Currently the main reason for method overloading to be present on the language is to provide a way to write declarations for libraries with magic arguments in their API. Since you'll need to do all the heavy-lifting by yourself to handle different sets of arguments I don't see much advantage in using overloads instead of separated methods.
比方说,TypeScript 中的方法重载并不是真的,因为它需要太多编译器生成的代码,而核心团队会不惜一切代价避免这种情况。目前,该语言中出现方法重载的主要原因是提供一种方法,可以为在 API 中带有魔法参数的库编写声明。由于您需要自己完成所有繁重的工作来处理不同的参数集,因此我认为使用重载而不是单独的方法没有太大的优势。
回答by Benson
Note: this was simplified and updated 4/13/2017 to reflect TypeScript 2.1, see history for TypeScript 1.8 answer.
注意:这是在 2017 年 4 月 13 日进行了简化和更新以反映 TypeScript 2.1,请参阅 TypeScript 1.8 答案的历史记录。
It sounds like you want the object parameter to be optional, and also each of the properties in the object to be optional. In the example, as provided, overload syntax isn't needed. I wanted to point out some bad practices in some of the answers here. Granted, it's not the smallest possible expression of essentially writing box = { x: 0, y: 87, width: 4, height: 0 }
, but this provides all the code hinting niceties you could possibly want from the class as described. This example allows you to call a function with one, some, all, ornone of the parameters and still get default values.
听起来您希望对象参数是可选的,并且对象中的每个属性也是可选的。在示例中,如提供的那样,不需要重载语法。我想在这里的一些答案中指出一些不好的做法。诚然,它不是本质上写作的最小可能表达box = { x: 0, y: 87, width: 4, height: 0 }
,但这提供了您可能希望从所述类中获得的所有代码提示细节。此示例允许您使用一个、部分、全部或不带参数调用函数,并且仍然获得默认值。
/** @class */
class Box {
public x?: number;
public y?: number;
public height?: number;
public width?: number;
// The Box class can work double-duty as the interface here since they are identical
// If you choose to add methods or modify this class, you will need to
// define and reference a new interface for the incoming parameters object
// e.g.: `constructor(params: BoxObjI = {} as BoxObjI)`
constructor(params: Box = {} as Box) {
// Define the properties of the incoming `params` object here.
// Setting a default value with the `= 0` syntax is optional for each parameter
let {
x = 0,
y = 0,
height = 1,
width = 1
} = params;
// If needed, make the parameters publicly accessible
// on the class ex.: 'this.var = var'.
/** Use jsdoc comments here for inline ide auto-documentation */
this.x = x;
this.y = y;
this.height = height;
this.width = width;
}
}
This is a very safe way to write for parameters that may not have all properties of the object defined. You can now safely write any of these:
对于可能没有定义对象的所有属性的参数,这是一种非常安全的写入方式。您现在可以安全地编写以下任何内容:
const box1 = new Box();
const box2 = new Box({});
const box3 = new Box({x:0});
const box4 = new Box({x:0, height:10});
const box5 = new Box({x:0, y:87,width:4,height:0});
// Correctly reports error in TypeScript, and in js, box6.z is undefined
const box6 = new Box({z:0});
Compiled, you see that the optional parameters truly are optional, that avoids the pitfalls of a widely used (but error prone) fallback syntax of var = isOptional || default;
by checking against void 0
, which is shorthand for undefined
:
编译后,您会看到可选参数确实是可选的,这避免了广泛使用(但容易出错)的var = isOptional || default;
by checkback 语法的陷阱void 0
,它是 的简写undefined
:
The Compiled Output
编译输出
var Box = (function () {
function Box(params) {
if (params === void 0) { params = {}; }
var _a = params.x, x = _a === void 0 ? 0 : _a, _b = params.y, y = _b === void 0 ? 0 : _b, _c = params.height, height = _c === void 0 ? 1 : _c, _d = params.width, width = _d === void 0 ? 1 : _d;
this.x = x;
this.y = y;
this.height = height;
this.width = width;
}
return Box;
}());
Addendum: Setting default values: the wrong way
附录:设置默认值:错误的方式
The ||
(or) operator
的||
(或)运算符
Consider the danger of ||
/or operators when setting default fallback values as shown in some other answers. This code below illustrates the wrong way to set defaults. You can get unexpected results when evaluating against falseyvalues like 0, '', null, undefined, false, NaN:
||
在设置默认回退值时考虑/or 运算符的危险,如其他一些答案所示。下面的代码说明了设置默认值的错误方法。针对评估时,可以得到意想不到的结果falsey如0值,“”,null,未定义,假的,楠:
var myDesiredValue = 0;
var result = myDesiredValue || 2;
// This test will correctly report a problem with this setup.
console.assert(myDesiredValue === result && result === 0, 'Result should equal myDesiredValue. ' + myDesiredValue + ' does not equal ' + result);
Object.assign(this,params)
Object.assign(this,params)
In my tests, using es6/typescript destructured object can be almost 90% faster than Object.assign. Using a destructured parameter only allows methods and properties you've assigned to the object. For example, consider this method:
在我的测试中,使用 es6/typescript 解构对象比 Object.assign 快近 90%。使用解构参数只允许您分配给对象的方法和属性。例如,考虑这个方法:
class BoxTest {
public x?: number = 1;
constructor(params: BoxTest = {} as BoxTest) {
Object.assign(this, params);
}
}
If another user wasn't using TypeScript and attempted to place a parameter that didn't belong, say, they might try putting a z
property
如果另一个用户没有使用 TypeScript 并试图放置一个不属于的参数,例如,他们可能会尝试放置一个z
属性
var box = new BoxTest({x: 0, y: 87, width: 4, height: 0, z: 7});
// This test will correctly report an error with this setup. `z` was defined even though `z` is not an allowed property of params.
console.assert(typeof box.z === 'undefined')
回答by ShinNoNtheitroad
Note that you can also work around the lack of overloading at the implementation level through default parameters in TypeScript, e.g.:
请注意,您还可以通过 TypeScript 中的默认参数来解决实现级别缺少重载的问题,例如:
interface IBox {
x : number;
y : number;
height : number;
width : number;
}
class Box {
public x: number;
public y: number;
public height: number;
public width: number;
constructor(obj : IBox = {x:0,y:0, height:0, width:0}) {
this.x = obj.x;
this.y = obj.y;
this.height = obj.height;
this.width = obj.width;
}
}
Edit:As of Dec 5 '16, see Benson's answerfor a more elaborate solution that allows more flexibility.
编辑:截至 2016 年 12 月 5 日,请参阅Benson 的回答以获取更详细的解决方案,该解决方案允许更大的灵活性。
回答by Joe
I know this is an old question, but new in 1.4 is union types; use these for all function overloads (including constructors). Example:
我知道这是一个老问题,但 1.4 中的新问题是联合类型;将这些用于所有函数重载(包括构造函数)。例子:
class foo {
private _name: any;
constructor(name: string | number) {
this._name = name;
}
}
var f1 = new foo("bar");
var f2 = new foo(1);
回答by vegemite4me
Update (8 June 2017):guyarad and snolflake make valid points in their comments below to my answer. I would recommend readers look at the answers by Benson, Joeand snolflakewho have better answers than mine.
更新(2017 年 6 月 8 日):Guyarad 和 snolflake 在下面的评论中对我的回答提出了有效观点。我建议读者看看Benson、Joe和snolflake的答案,他们的答案比我的好。
Original Answer (27 January 2014)
原始答案(2014 年 1 月 27 日)
Another example of how to achieve constructor overloading:
如何实现构造函数重载的另一个例子:
class DateHour {
private date: Date;
private relativeHour: number;
constructor(year: number, month: number, day: number, relativeHour: number);
constructor(date: Date, relativeHour: number);
constructor(dateOrYear: any, monthOrRelativeHour: number, day?: number, relativeHour?: number) {
if (typeof dateOrYear === "number") {
this.date = new Date(dateOrYear, monthOrRelativeHour, day);
this.relativeHour = relativeHour;
} else {
var date = <Date> dateOrYear;
this.date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
this.relativeHour = monthOrRelativeHour;
}
}
}
Source: http://mimosite.com/blog/post/2013/04/08/Overloading-in-TypeScript
来源:http: //mimosite.com/blog/post/2013/04/08/Overloading-in-TypeScript
回答by Kostas Drak
Actually it might be too late for this answer but you can now do this:
实际上,这个答案可能为时已晚,但您现在可以这样做:
class Box {
public x: number;
public y: number;
public height: number;
public width: number;
constructor();
constructor(obj: IBox);
constructor(obj?: IBox) {
this.x = !obj ? 0 : obj.x;
this.y = !obj ? 0 : obj.y;
this.height = !obj ? 0 : obj.height;
this.width = !obj ? 0 : obj.width;
}
}
so instead of static methods you can do the above. I hope it will help you!!!
因此,您可以执行上述操作,而不是静态方法。希望对你有帮助!!!
回答by Yacine
You can handle this by :
您可以通过以下方式处理:
import { assign } from 'lodash'; // if you don't have lodash use Object.assign
class Box {
x: number;
y: number;
height: number;
width: number;
constructor(obj: Partial<Box> = {}) {
assign(this, obj);
}
}
Partial will make your fields (x,y, height, width) optionals, allowing multiple constructors
Partial 将使您的字段 (x,y, height, width) 成为可选项,允许多个构造函数
eg: you can do new Box({x,y})
without height, and width.
例如:你可以new Box({x,y})
不用高度和宽度。
The = {}
will handle falsy value such as undefined, null etc, and then you can do new Box()
该= {}
会处理,如不定falsy值,空等等,然后你可以做new Box()
回答by Mudlabs
Your Box
class is attempting to define multiple constructor implementations.
您的Box
类正在尝试定义多个构造函数实现。
Only the last constructor overload signatureis used as the class constructor implementation.
只有最后一个构造函数重载签名用作类构造函数实现。
In the below example, note the constructor implementationis defined such that it does notcontradict either of the preceding overload signatures.
在下面的示例中,请注意构造函数实现被定义为使得它不矛盾任一前述的过载的签名。
interface IBox = {
x: number;
y: number;
width: number;
height: number;
}
class Box {
public x: number;
public y: number;
public width: number;
public height: number;
constructor() /* Overload Signature */
constructor(obj: IBox) /* Overload Signature */
constructor(obj?: IBox) /* Implementation Constructor */ {
if (obj) {
this.x = obj.x;
this.y = obj.y;
this.width = obj.width;
this.height = obj.height;
} else {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0
}
}
get frame(): string {
console.log(this.x, this.y, this.width, this.height);
}
}
new Box().frame; // 0 0 0 0
new Box({ x:10, y:10, width: 70, height: 120 }).frame; // 10 10 70 120
// You could also write the Box class like so;
class Box {
public x: number = 0;
public y: number = 0;
public width: number = 0;
public height: number = 0;
constructor() /* Overload Signature */
constructor(obj: IBox) /* Overload Signature */
constructor(obj?: IBox) /* Implementation Constructor */ {
if (obj) {
this.x = obj.x;
this.y = obj.y;
this.width = obj.width;
this.height = obj.height;
}
}
get frame(): string { ... }
}
回答by parliament
In the case where an optional, typed parameter is good enough, consider the following code which accomplishes the same without repeating the properties or defining an interface:
如果可选的类型参数足够好,请考虑以下代码,该代码无需重复属性或定义接口即可完成相同的操作:
export class Track {
public title: string;
public artist: string;
public lyrics: string;
constructor(track?: Track) {
Object.assign(this, track);
}
}
Keep in mind this will assign all properties passed in track
, eve if they're not defined on Track
.
请记住,这将分配传入的所有属性track
,如果它们未在 上定义,则为前夕Track
。