Typescript:union构造函数类型

时间:2016-03-29 14:16:09

标签: typescript

我正在尝试在typescript中创建一个小的不可变类:

import * as _ from "lodash";

export class Immutable<T> {

  constructor(public data:T) {
    Object.freeze(data);

    _.each(_.keysIn(data), (key) => {
      Object.defineProperty(this, key, <PropertyDescriptor> {get: () => this.data[key]})
    })
  }

  set(key:string, val:any):Immutable<T> {
    if (_.isEqual(this.data[key], val)) {
      return this;
    }

    let newData = {};
    newData[key] = val;
    return new Immutable<T>(_.defaults<T>(newData, this.data))
  }

  update(data:T) {
    let newVal = _.defaults<T>(data, this.data);

    return _.isEqual(this.data, newVal) ? this : new Immutable<T>(newVal);
  }

  get(key):any {
    return this.data[key];
  }

  toJson():T {
    return this.data;
  }
}

现在,我必须在创建像const instance =<Immutable<{x: number}> & {x: number}> new Immutable({x: 1});这样的不可变项时手动添加T,以便我可以使用instance.x访问x。我知道有一种方法可以将new (): Immutable<T> & T定义为构造函数方法,但我只是找不到我记忆中的资源了。有人能指出我正确的方向吗?

谢谢你,罗宾

修改

有趣的是,现在通过不可变的作品访问T的属性,虽然我真的不明白为什么(打字稿能理解构造函数中的Object.defineProperty吗?)。

我更新了类以启用子类化并在那里设置默认值,如果有人感兴趣的话:

import * as _ from "lodash";

export class Immutable<T> {
  constructor(public data:T) {
    Object.freeze(data);


    _.each(_.keysIn(data), (key) => {
      Object.defineProperty(this, key, <PropertyDescriptor> {get: () => this.data[key]})
    })
  }

  set(key:string, val:any):this {
    if (_.isEqual(this.data[key], val)) {
      return this;
    }

    const newData = _.defaults<T>(_.fromPairs([[key, val]]), this.data);
    return this.new(newData)
  }

  update(data:T):this {
    const newData = _.defaults<T>(data, this.data);
    return _.isEqual(this.data, newData) ? this : this.new(newData);
  }

  new(...args:any[]):this {
    return <this> (new (<any>this.constructor)(...args));
  }

  get(key):any {
    return this.data[key];
  }

  toJson():T {
    return this.data;
  }
}

这使得s .th。像这样可能:

class Child extends Immutable<{x:number}> {
  constructor(data = {x: 1}) {
    super(data)
  }
}

我保持问题是开放的,因为我仍然想知道如何使得Typescript知道导出的类具有比定义更多的属性的答案(可能是外部添加或者像我一样通过构造函数添加)

2 个答案:

答案 0 :(得分:0)

正如您所发现的那样,类型系统不允许您说类本身为扩展了T。您可以使用function来破解内容。但请不要。例如。如果update上存在T属性,则会与您的Immutable.update冲突。

答案 1 :(得分:0)

您无法定义构造函数的返回类型,但是,您可以使用静态工厂方法来实现所需:

&#13;
&#13;
export class Immutable<T> {
    // You can protect your constructor.
    protected constructor(public data: T) {
        Object.freeze(data);

        _.each(_.keysIn(data), (key) => {
            Object.defineProperty(this, key, <PropertyDescriptor>{ get: () => this.data[key] })
        })
    }

    static create<T>(data: T): Immutable<T> & T {
        return new (<any>Immutable)(data);
    }

    // Another super useful new feature in TypeScript 2.1: keyof, refer to the usage below.
    get<K extends keyof T>(key: K): T[K] {
        return this.data[key];
    }

    // ...
}

// Usage:
var obj = Immutable.create({ x: 1 });
console.log(obj.x);
console.log(obj.get("x"));

// TypeScript will generate compilation error: 
// Argument of type '""' is not assignable to parameter of type '"x"'.
console.log(obj.get("y"));
&#13;
&#13;
&#13;