打字稿:通过传入命名参数的构造函数创建class?
Typescript: Create class via constructor passing in named parameters?
我有一个 class,其中我定义了构造函数,其中包含 3 个参数,这些参数都是可选的。我希望能够传入命名参数,这样我就不需要传入 undefined 了。
constructor(year?: number,
month?: number,
date?: number)
我希望像这样创建 class 的实例
const recurrenceRule = new MyNewClass(month: 6)
但是没用,我试过了
const recurrenceRule = new MyNewClass(month = 6)
那没用。
我让它工作的唯一方法是
const recurrenceRule = new MyNewClass(undefined, 4)
或
const recurrenceRule = new MyNewClass(, 4)
但它看起来很乱,我希望传递命名参数,因为它们都是可选的,所以我应该可以只传递 1 - 对吗?
class Bar {
constructor({a, b}: {a?: number, b?: number}) {}
}
new Bar({b: 1})
有关详细信息,请参阅 ES6 Object Destructuring with functions。
您可以使用 ES6 中引入的对象解构来实现所需的行为:reference。 TypeScript 能够将此功能转换为与 ES5 一起使用以针对较旧的浏览器。然而,从 ES6 开始,这也是完全有效的 JavaScript.
基本上,它看起来像这样:constructor({ year, month, day})
并被调用,例如,new Bar({ year: 2017 })
。然后您可以在构造函数中将 year
作为变量访问,例如用于分配 this.year = year
.
比这更有趣的是默认值的用法,例如
constructor({ year = new Date().getFullYear(),
month = new Date().getMonth(),
day = new Date().getDay()
} = {})
它允许分别使用 0、1、2 或 3 个参数调用构造函数(参见下面的代码片段)。
有点神秘的 = {}
适用于创建不带任何参数的新实例的情况。首先,{}
用作参数对象的默认值。然后,由于缺少 year
,因此添加了该值的默认值,然后分别添加了月份和日期。
对于 TypeScript 的使用,您当然可以添加额外的类型,
constructor({ year = new Date().getFullYear(),
month = new Date().getMonth(),
day = new Date().getDay()
}: { year?: number, month?: number, day?: number } = {}) {
...
}
虽然这个真的看起来很神秘。
class Bar {
constructor({ year, month, day }) {
this.year = year;
this.month = month;
this.day = day;
}
log () {
console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
}
}
new Bar({ day: 2017 }).log();
class Foo {
constructor({ year = new Date().getFullYear(),
month = new Date().getMonth(),
day = new Date().getDay()
} = {}) {
this.year = year;
this.month = month;
this.day = day;
}
log () {
console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
}
}
console.log('with default value:');
new Foo().log();
new Foo({ day: 2 }).log();
new Foo({ day: 2, month: 8 }).log();
new Foo({ year: 2015 }).log();
简单参数:
constructor (private recurrenceSettings: {year?: number, month?: number, date?: number})
private 关键字将参数实例化为实例变量,省去了在构造函数中实例化的需要。如果要实例化 public 属性,也可以是 public
。
这样使用:
const recurrenceRule = new MyClass({month: 12})
或使用解构(用法同上):
constructor({day, month, year}: {day?: number, month?: number, year?: number})
以上版本失去了对实例变量使用 private/public 快捷方式的能力(参见 https://github.com/Microsoft/TypeScript/issues/5326)。
您也可以使用 Partial 来获得相同的结果。
您将以与之前答案相同的方式实例化。
class Bar {
year = new Date().getFullYear();
month = new Date().getMonth();
day = new Date().getDay();
constructor(bar?: Partial<Bar>) {
Object.assign(this, bar);
}
log () {
console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
}
}
new Bar().log();
new Bar({ day: 2 }).log();
new Bar({ day: 2, month: 8 }).log();
new Bar({ year: 2015 }).log();
注意 1. Partial<Bar>
接受任何类型的同名属性。是的,这有点违反类型安全。这仍然很强大的原因是,如果您开始使用 {
键入对象文字,智能感知会告诉您可以初始化哪些属性。
注意 2. Object.assign
将忽略 readonly
属性,因为它是 JavaScript 特定的东西,而不是 TypeScript 特定的。这意味着虽然您不能为 readonly
属性 分配一个新值,但您当然可以使用 Object.assign
.
在某些情况下,拥有一个接口和单独的制造商功能会更容易。 (对于未来的读者。)
interface Foo {
x: number
y: number
}
function newFoo({x=5,y=10}) : Foo {return {x,y}}
const f = newFoo({x:100})
看起来这仍然不是那么容易,尽管它会很好。
但解决方案只用了几分钟。
这是我所做的。我使用 来获取属性减去方法的列表。
export class Member {
private constructor(public memberID: number = -1,
public email: string = '',
public loggedIn: boolean = false) {
}
static newByNamed(obj: NonFunctionProperties<Member>) {
Object.assign(new Member(), obj);
}
public clear() {
this.memberID = config.nonExistentID;
this.email = '';
this.loggedIn = false;
}
}
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
然后我可以用
调用它
let member = Member.newByNamed({email = 'me@home'...});
我有一个 class,其中我定义了构造函数,其中包含 3 个参数,这些参数都是可选的。我希望能够传入命名参数,这样我就不需要传入 undefined 了。
constructor(year?: number,
month?: number,
date?: number)
我希望像这样创建 class 的实例
const recurrenceRule = new MyNewClass(month: 6)
但是没用,我试过了
const recurrenceRule = new MyNewClass(month = 6)
那没用。
我让它工作的唯一方法是
const recurrenceRule = new MyNewClass(undefined, 4)
或
const recurrenceRule = new MyNewClass(, 4)
但它看起来很乱,我希望传递命名参数,因为它们都是可选的,所以我应该可以只传递 1 - 对吗?
class Bar {
constructor({a, b}: {a?: number, b?: number}) {}
}
new Bar({b: 1})
有关详细信息,请参阅 ES6 Object Destructuring with functions。
您可以使用 ES6 中引入的对象解构来实现所需的行为:reference。 TypeScript 能够将此功能转换为与 ES5 一起使用以针对较旧的浏览器。然而,从 ES6 开始,这也是完全有效的 JavaScript.
基本上,它看起来像这样:constructor({ year, month, day})
并被调用,例如,new Bar({ year: 2017 })
。然后您可以在构造函数中将 year
作为变量访问,例如用于分配 this.year = year
.
比这更有趣的是默认值的用法,例如
constructor({ year = new Date().getFullYear(),
month = new Date().getMonth(),
day = new Date().getDay()
} = {})
它允许分别使用 0、1、2 或 3 个参数调用构造函数(参见下面的代码片段)。
有点神秘的 = {}
适用于创建不带任何参数的新实例的情况。首先,{}
用作参数对象的默认值。然后,由于缺少 year
,因此添加了该值的默认值,然后分别添加了月份和日期。
对于 TypeScript 的使用,您当然可以添加额外的类型,
constructor({ year = new Date().getFullYear(),
month = new Date().getMonth(),
day = new Date().getDay()
}: { year?: number, month?: number, day?: number } = {}) {
...
}
虽然这个真的看起来很神秘。
class Bar {
constructor({ year, month, day }) {
this.year = year;
this.month = month;
this.day = day;
}
log () {
console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
}
}
new Bar({ day: 2017 }).log();
class Foo {
constructor({ year = new Date().getFullYear(),
month = new Date().getMonth(),
day = new Date().getDay()
} = {}) {
this.year = year;
this.month = month;
this.day = day;
}
log () {
console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
}
}
console.log('with default value:');
new Foo().log();
new Foo({ day: 2 }).log();
new Foo({ day: 2, month: 8 }).log();
new Foo({ year: 2015 }).log();
简单参数:
constructor (private recurrenceSettings: {year?: number, month?: number, date?: number})
private 关键字将参数实例化为实例变量,省去了在构造函数中实例化的需要。如果要实例化 public 属性,也可以是 public
。
这样使用:
const recurrenceRule = new MyClass({month: 12})
或使用解构(用法同上):
constructor({day, month, year}: {day?: number, month?: number, year?: number})
以上版本失去了对实例变量使用 private/public 快捷方式的能力(参见 https://github.com/Microsoft/TypeScript/issues/5326)。
您也可以使用 Partial 来获得相同的结果。
您将以与之前答案相同的方式实例化。
class Bar {
year = new Date().getFullYear();
month = new Date().getMonth();
day = new Date().getDay();
constructor(bar?: Partial<Bar>) {
Object.assign(this, bar);
}
log () {
console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
}
}
new Bar().log();
new Bar({ day: 2 }).log();
new Bar({ day: 2, month: 8 }).log();
new Bar({ year: 2015 }).log();
注意 1. Partial<Bar>
接受任何类型的同名属性。是的,这有点违反类型安全。这仍然很强大的原因是,如果您开始使用 {
键入对象文字,智能感知会告诉您可以初始化哪些属性。
注意 2. Object.assign
将忽略 readonly
属性,因为它是 JavaScript 特定的东西,而不是 TypeScript 特定的。这意味着虽然您不能为 readonly
属性 分配一个新值,但您当然可以使用 Object.assign
.
在某些情况下,拥有一个接口和单独的制造商功能会更容易。 (对于未来的读者。)
interface Foo {
x: number
y: number
}
function newFoo({x=5,y=10}) : Foo {return {x,y}}
const f = newFoo({x:100})
看起来这仍然不是那么容易,尽管它会很好。
但解决方案只用了几分钟。
这是我所做的。我使用
export class Member {
private constructor(public memberID: number = -1,
public email: string = '',
public loggedIn: boolean = false) {
}
static newByNamed(obj: NonFunctionProperties<Member>) {
Object.assign(new Member(), obj);
}
public clear() {
this.memberID = config.nonExistentID;
this.email = '';
this.loggedIn = false;
}
}
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
然后我可以用
调用它let member = Member.newByNamed({email = 'me@home'...});