Typescript:通过传入命名参数的构造函数创建类?

问题描述 投票:0回答:6

我有一个类,其中定义了 3 个参数的构造函数,这些参数都是可选的。我希望能够传入命名参数,这样我就不需要传入未定义的参数。

constructor(year?: number,
            month?: number,
            date?: number)

我希望像这样创建一个类的实例

  const recurrenceRule = new MyNewClass(month: 6)

但它不起作用,我尝试了

  const recurrenceRule = new MyNewClass(month = 6)

但那不起作用。

我让它工作的唯一方法是

  const recurrenceRule = new MyNewClass(undefined, 4)

  const recurrenceRule = new MyNewClass(, 4)

但是看起来很混乱,我希望传入命名参数,因为它们都是可选的,我应该能够只传入 1 - 对吗?

typescript named-parameters
6个回答
67
投票

您可以使用 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();


23
投票
class Bar {
  constructor({a, b}: {a?: number, b?: number}) {}
}

new Bar({b: 1})

有关更多信息,请参阅使用函数解构 ES6 对象


6
投票

参数简单:

constructor (private recurrenceSettings: {year?: number, month?: number, date?: number})

private 关键字将参数实例化为实例变量,从而无需在构造函数中实例化。如果您想实例化公共属性,也可以是

public

像这样使用:

const recurrenceRule = new MyClass({month: 12})

或者使用解构(用法同上):

constructor({day, month, year}: {day?: number, month?: number, year?: number})

上述版本失去了使用实例变量的私有/公共快捷方式的能力(请参阅https://github.com/Microsoft/TypeScript/issues/5326)。


5
投票

您也可以使用 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


4
投票

虽然这会很好,但似乎这仍然没有那么容易实现。

但解决方案只花了几分钟。

这就是我所做的..我使用这个很棒的答案来获取属性减去方法的列表。

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'...});

0
投票

在某些情况下,拥有一个界面和单独的制作功能会更容易。 (对于未来的读者。)


interface Foo {
  x: number
  y: number
}

function newFoo({x=5,y=10}) : Foo {return {x,y}}

const f = newFoo({x:100})
© www.soinside.com 2019 - 2024. All rights reserved.