Class 在TypeScript中声明动态添加的类属性

Class 在TypeScript中声明动态添加的类属性,class,typescript,properties,typescript-typings,tsc,Class,Typescript,Properties,Typescript Typings,Tsc,我希望在不知道TypeScript中的属性名称、值和值类型的情况下为类的实例分配属性。假设我们有以下示例.ts脚本: // This could be a server response and could look totally diffent another time... const someJson:string = '{ "foo": "bar", "bar": "baz" }' class MyClass { someProperty:boolean construct

我希望在不知道TypeScript中的属性名称、值和值类型的情况下为类的实例分配属性。假设我们有以下
示例.ts
脚本:

// This could be a server response and could look totally diffent another time...
const someJson:string = '{ "foo": "bar", "bar": "baz" }'

class MyClass {
  someProperty:boolean

  constructor( json:string ) {
    const parsedJson:any = JSON.parse( json )

    Object.keys( parsedJson ).forEach(
      ( key:string ) => {
        this[ key ] = parsedJson[ key ]
      }
    )

    this['someProperty'] = true
  }
}

const myInstance = new MyClass( someJson )

// Works fine, logs `true`.
console.log( myInstance.someProperty )

// Error: Property 'foo' does not exist on type 'MyClass'.
console.log( myInstance.foo )

// Error: Property 'bar' does not exist on type 'MyClass'.
console.log( myInstance.bar )
如何确保TypeScript编译器不会抱怨动态添加的属性,而是将它们作为任何类型的
“key”:value
对处理。我仍然希望
tsc
确保
myInstance.someProperty
必须是
boolean
类型,但我希望能够获取
myInstance.whatever
,即使它没有定义,也不会出现编译器错误

我没有找到任何文件,使我清楚这一点。也许是因为我不是以英语为母语的人。所以请保持答案简单

编辑:

我记得曾经有过这样的事情,但我从来没有做到:

interface IMyClass {
  [name:string]: any
}

问题是您正在运行时添加新属性,而编译器无法知道这一点

如果您事先知道属性名称,则可以执行以下操作:

type Json = {
    foo: string;
    bar: string;
}

...

const myInstance = new MyClass(someJson) as MyClass & Json;
console.log(myInstance.foo) // no error
const myInstance = new MyClass(someJson) as MyClass & { [key: string]: string };
let foo = myInstance["foo"]; // type of foo is string
let someProperty = myInstance["someProperty"]; // type of someProperty is boolean

编辑 如果您事先不知道属性,则无法执行此操作:

console.log(myInstance.foo);
因为您知道
foo
是接收到的json的一部分,所以您可能会有如下内容:

let key = getKeySomehow();
console.log(myInstance[key]);
这应该在没有编译器错误的情况下工作,唯一的问题是编译器不知道返回值的类型,它将是
any

所以你可以这样做:

type Json = {
    foo: string;
    bar: string;
}

...

const myInstance = new MyClass(someJson) as MyClass & Json;
console.log(myInstance.foo) // no error
const myInstance = new MyClass(someJson) as MyClass & { [key: string]: string };
let foo = myInstance["foo"]; // type of foo is string
let someProperty = myInstance["someProperty"]; // type of someProperty is boolean

第二版 正如你知道的道具,但不是在课堂上,你可以做:

type ExtendedProperties<T> = { [P in keyof T]: T[P] };
function MyClassFactory<T>(json: string): MyClass & ExtendedProperties<T> {
    return new MyClass(json) as MyClass & ExtendedProperties<T>;
}
type ExtendedProperties={[P in keyof T]:T[P]};
函数MyClassFactory(json:string):MyClass和ExtendedProperties{
将新的MyClass(json)作为MyClass和ExtendedProperties返回;
}
然后你就这么简单地使用它:

type Json = {
    foo: string;
    bar: string;
};
const myInstance = MyClassFactory<Json>(someJson);
type Json={
foo:string;
酒吧:字符串;
};
const myInstance=MyClassFactory(someJson);

请注意,这仅适用于typescript 2.1及以上版本。

如果您希望在实例化时通过对象动态添加类属性,并且该对象的类型信息可用,则可以通过这种方式很好地获得完整的类型安全性(只要您不介意使用静态工厂方法):

类可扩充{
构造函数(augment:any={}){
分配(此,扩充)
}
静态创建(this:T,augment?:U){
以InstanceType&U形式返回新的此(增强)
}
}
这是使用(假)来推断类的构造函数类型。然后,它构造实例,并将其强制转换为实例类型(使用
InstanceType
实用程序类型)和传递给该方法的支柱的推断类型的并集

(我们可以直接转换为
Augmentable&U
,但是这种方式允许我们扩展类。)

例子 扩充基本属性:

const hasIdProp = Augmentable.create({ id: 123 })
hasIdProp.id // number
增加方法:

const with addedMethod=Augmentable.create({
你好{
返回“你好,世界!”
}
})
withAddedMethod.sayHello()//正确键入,带有签名和返回值
扩展和扩充,使用
方法中的访问扩充:

类Bob扩展了可扩充的{
name='Bob'
override='从类定义设置'
checkOverrideFromDefinition(){
返回此项。覆盖
}
}
界面边界{
怎么说:绳子
覆盖:字符串
sayHelloTo(to:string):无效
checkOverrideRomaugment():字符串
}
常数bobAugment:bobAugment={
“你好”是什么意思,
覆盖:“从增强设置”
sayHelloTo(这是:Bob&BobAugment,to:string){
//让我们组合一个类参数、增强参数和函数参数!
将“${this.name}表示“${this.whatToSay}”的内容返回到${to}`
},
CheckOverrideFromagment(此:Bob&BobAugment){
返回此项。覆盖
}
}
const bob=bob.create(bobAugment)//键入为bob&bobAugment
bob.sayHelloTo(“Alice”)/“bob向Alice打招呼!”
//由于扩展类构造函数总是在父构造函数之后运行,
//无论如何,都不能用增广覆盖类集参数
//从你要检查的地方。
bob.checkOverrideFromAugment()/“从类定义设置”
bob.checkOverrideFromDefinition()/“从类定义设置”
局限性 扩充属性并不是类的一部分,因此不能扩展包含这些扩充的类。对于某些用例来说,这可能是一个特性,在这些用例中,扩充是临时添加,并不意味着修改原型层次结构

.create()
添加非增强参数也不容易,但是一个简单的解决方法是简单地利用增强功能来完成与使用额外参数相同的任务。

您可以向类中添加:

class MyClass {
  [index: string]: any; //index signature

  someProperty:boolean

  constructor( json:string ) {
    const parsedJson:any = JSON.parse( json )

    Object.keys( parsedJson ).forEach(
      ( key:string ) => {
        this[ key ] = parsedJson[ key ]
      }
    )

    this['someProperty'] = true
  }
}

有趣的答案。但是我没有办法事先知道这些属性。我认为在运行时扩展具有未知属性名的对象对普通JavaScript来说应该不是那么奇怪,所以我想知道为什么这在TypeScript中是个问题。因此,唯一的方法是将
myInstance.foo
转换为
any
,而不是
myInstance['foo']
,对吗?如果您使用
myInstance.foo
,那么这意味着您事先知道
foo
将在json中,因此您可以使用我在原始答案中显示的方法。如果你不知道它会在那里,那么你怎么能在你的代码中有它呢?我明白了。。。谢谢你的解释。我的代码只是我目前正在处理的节点模块的一个抽象示例,它应该在实例化时加载内容。因此,在导入并在另一个文件中使用该类时,我知道该类中有哪些属性,但该类本身不知道将为其指定哪些属性和类型。答案并没有解决这个问题。Typescript并没有一个很好的答案。但我投了赞成票,因为似乎没有