我正在尝试在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知道导出的类具有比定义更多的属性的答案(可能是外部添加或者像我一样通过构造函数添加)
答案 0 :(得分:0)
正如您所发现的那样,类型系统不允许您说类本身为类扩展了T。您可以使用function
来破解内容。但请不要。例如。如果update
上存在T
属性,则会与您的Immutable.update
冲突。
答案 1 :(得分:0)
您无法定义构造函数的返回类型,但是,您可以使用静态工厂方法来实现所需:
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;