如何在 TypeScript 中为对象动态分配属性?
Posted
技术标签:
【中文标题】如何在 TypeScript 中为对象动态分配属性?【英文标题】:How do I dynamically assign properties to an object in TypeScript? 【发布时间】:2015-06-29 12:00:50 【问题描述】:如果我想以编程方式将属性分配给 javascript 中的对象,我会这样做:
var obj = ;
obj.prop = "value";
但在 TypeScript 中,这会产生错误:
“”类型的值上不存在属性“prop”
我应该如何将任何新属性分配给 TypeScript 中的对象?
【问题讨论】:
【参考方案1】:索引类型
可以将obj
表示为any
,但这违背了使用打字稿的全部目的。 obj =
暗示 obj
是 Object
。将其标记为any
毫无意义。为了实现所需的一致性,可以按如下方式定义接口。
interface LooseObject
[key: string]: any
var obj: LooseObject = ;
或使其紧凑:
var obj: [k: string]: any = ;
LooseObject
可以接受任何字符串作为键,any
类型作为值的字段。
obj.prop = "value";
obj.prop2 = 88;
此解决方案的真正优雅之处在于您可以在界面中包含类型安全字段。
interface MyType
typesafeProp1?: number,
requiredProp1: string,
[key: string]: any
var obj: MyType ;
obj = requiredProp1: "foo"; // valid
obj = // error. 'requiredProp1' is missing
obj.typesafeProp1 = "bar" // error. typesafeProp1 should be a number
obj.prop = "value";
obj.prop2 = 88;
Record<Keys,Type>
实用程序类型
更新(2020 年 8 月):@transang 在 cmets 中提出了这个问题
Record<Keys,Type>
是 typescript 中的 Utility 类型。对于属性名称未知的键值对来说,它是一种更简洁的替代方案。 值得注意的是Record<Keys,Type>
是[k: Keys]: Type
的命名别名,其中Keys
和Type
是泛型。 IMO,这值得在这里提及
为了比较,
var obj: [k: string]: any = ;
变成
var obj: Record<string,any> =
MyType
现在可以通过扩展 Record 类型来定义
interface MyType extends Record<string,any>
typesafeProp1?: number,
requiredProp1: string,
虽然这回答了原始问题,但@GreeneCreations 的回答here 可能会为解决问题提供另一种观点。
【讨论】:
我认为这是目前最好的解决方案。我认为当时被问到这个问题的索引属性还没有在 TypeScript 中实现。 当你有动态数据时它确实有意义。如果您从 API 接收动态数据,构建表单,然后将动态值发送回 api,这确实有意义。 Any 是有原因的,显然你应该强输入大多数不是动态的东西,但在动态情况下你不能强输入。 很好的解决方案。漂亮又简单 现在,你可以写Record<string, any>
,而不是[key: string]: any
8 年后,这个答案拯救了我的一天!谢谢!【参考方案2】:
或一次性完成:
var obj:any =
obj.prop = 5;
【讨论】:
如果我必须将这么多东西投射到any
才能使用它,那么 TypeScript 的意义何在?只是在我的代码中变成了额外的噪音..:/
@AjaxLeung 如果你需要这样做,那是你用错了。
查看上面的附加编辑,保留对象以前的类型。
@AjaxLeung 你应该很少投到any
。 TypeScript 用于在编译时捕获(潜在的)错误。如果你使用any
来消除错误,那么你就失去了打字的能力,还不如回到纯JS。 any
理想情况下只应在您导入无法编写 TS 定义的代码或将代码从 JS 迁移到 TS 时使用【参考方案3】:
当您的对象具有特定类型时,此解决方案很有用。就像将对象获取到其他来源时一样。
let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
【讨论】:
这是一次性作业的正确解决方案。 这个答案对我有用,因为对于 facebook 登录,我必须向 window 对象添加属性。我第一次在 TypeScript 中使用 as 关键字。【参考方案4】:我倾向于将any
放在另一边,即var foo:IFoo = <any>;
所以这样的东西仍然是类型安全的:
interface IFoo
bar:string;
baz:string;
boo:string;
// How I tend to intialize
var foo:IFoo = <any>;
foo.bar = "asdf";
foo.baz = "boo";
foo.boo = "boo";
// the following is an error,
// so you haven't lost type safety
foo.bar = 123;
或者,您可以将这些属性标记为可选:
interface IFoo
bar?:string;
baz?:string;
boo?:string;
// Now your simple initialization works
var foo:IFoo = ;
Try it online
【讨论】:
+1 是唯一保持类型安全的解决方案。只需确保在它之后直接实例化所有非可选属性,以避免稍后出现错误。 这真的有效吗?编译后,我的 javascript 中仍然有 。I still have <any>
那么你还没有编译。 TypeScript 会在它的发射中删除它
如今,var foo: IFoo = as any
是首选。用于类型转换的旧语法与 TSX(Typescript-ified JSX)发生冲突。【参考方案5】:
尽管编译器抱怨它仍应按您的要求输出。但是,这将起作用。
var s = ;
s['prop'] = true;
【讨论】:
是的,这不是真正的类型脚本方式,你失去了智能感知。 适用于具有动态结构的对象(不能在接口中定义) 不要使用 var.. 而是使用 let 或 const【参考方案6】:另一种选择是将属性作为集合访问:
var obj = ;
obj['prop'] = "value";
【讨论】:
这是最简洁的方式。来自 ES6/ES2015 的Object.assign(obj, prop: "value")
也可以。【参考方案7】:
我很惊讶没有一个答案引用 Object.assign,因为这是我在考虑 JavaScript 中的“组合”时使用的技术。
它在 TypeScript 中按预期工作:
interface IExisting
userName: string
interface INewStuff
email: string
const existingObject: IExisting =
userName: "jsmith"
const objectWithAllProps: IExisting & INewStuff = Object.assign(, existingObject,
email: "jsmith@someplace.com"
)
console.log(objectWithAllProps.email); // jsmith@someplace.com
优势
始终键入安全,因为您根本不需要使用any
类型
使用 TypeScript 的聚合类型(在声明 objectWithAllProps
的类型时由 &
表示),这清楚地表明我们正在即时(即动态)组合新类型
注意事项
-
Object.assign 有它自己独特的方面(大多数有经验的 JS 开发人员都知道),在编写 TypeScript 时应该考虑这些方面。
它可以以可变方式或不可变方式使用(我在上面演示了不可变方式,这意味着
existingObject
保持不变,因此没有email
属性。对于大多数函数式程序员,这是一件好事,因为结果是唯一的新变化)。
Object.assign 在您拥有更扁平的对象时效果最佳。如果您正在组合两个包含可为空属性的嵌套对象,您最终可能会用 undefined 覆盖真实值。如果你注意 Object.assign 参数的顺序,你应该没问题。
【讨论】:
对我来说,在您需要使用它来显示数据的情况下,它似乎工作正常,但是当您需要将修改类型的条目添加到数组时,这似乎也不起作用好吧。我采用动态组合对象并将其分配在一行中,然后在连续的行中分配动态属性。这让我大部分时间都在那里,所以谢谢你。【参考方案8】:您可以使用扩展运算符基于旧对象创建新对象
interface MyObject
prop1: string;
const myObj: MyObject =
prop1: 'foo',
const newObj =
...myObj,
prop2: 'bar',
console.log(newObj.prop2); // 'bar'
TypeScript 会推断出原始对象的所有字段,VSCode 会做自动补全等。
【讨论】:
很好,但如果您需要在例如使用 prop2 prop3,将难以实施 不确定我是否遵循该声明 - 在此示例中没有prop3
。【参考方案9】:
你可以用这个:
this.model = Object.assign(this.model, newProp: 0 );
【讨论】:
你不需要this.model =
。
this.model = ...this.model, newProp: 0 ;
【参考方案10】:
因为你不能这样做:
obj.prop = 'value';
如果你的 TS 编译器和你的 linter 不严格你,你可以这样写:
obj['prop'] = 'value';
如果您的 TS 编译器或 linter 是严格的,另一个答案是类型转换:
var obj = ;
obj = obj as unknown as prop: string ;
obj.prop = "value";
【讨论】:
这是如果 tsconfig.json 上的 'noImplicitAny: false' 否则你可以做 GreeneCreations 的回答。【参考方案11】:最简单的会跟着
const obj = <any>;
obj.prop1 = "value";
obj.prop2 = "another value"
【讨论】:
【参考方案12】:这是Object.assign
的一个特殊版本,它会随着每个属性的变化自动调整变量类型。不需要额外的变量、类型断言、显式类型或对象副本:
function assign<T, U>(target: T, source: U): asserts target is T & U
Object.assign(target, source)
const obj = ;
assign(obj, prop1: "foo" )
// const obj now has type prop1: string;
obj.prop1 // string
assign(obj, prop2: 42 )
// const obj now has type prop1: string; prop2: number;
obj.prop2 // number
// const obj: prop1: "foo", prop2: 42
注意:sample 使用 TS 3.7 assertion functions。 assign
的返回类型是 void
,与 Object.assign
不同。
【讨论】:
【参考方案13】:要保证类型是Object
(即键值对),请使用:
const obj: [x: string]: any =
obj.prop = 'cool beans'
【讨论】:
这个解决方案对我有用,因为没有额外的类型信息,我仍然得到这个错误:元素隐式地有一个'任何'类型,因为类型'字符串'的表达式不能用于索引类型' '。【参考方案14】:案例一:
var car = type: "BMW", model: "i8", color: "white";
car['owner'] = "ibrahim"; // You can add a property:
案例 2:
var car:any = type: "BMW", model: "i8", color: "white";
car.owner = "ibrahim"; // You can set a property: use any type
【讨论】:
【参考方案15】:可以通过以下方式将成员添加到现有对象中
-
扩大类型(阅读:扩展/专门化接口)
将原始对象转换为扩展类型
将成员添加到对象
interface IEnhancedPromise<T> extends Promise<T>
sayHello(): void;
const p = Promise.resolve("Peter");
const enhancedPromise = p as IEnhancedPromise<string>;
enhancedPromise.sayHello = () => enhancedPromise.then(value => console.info("Hello " + value));
// eventually prints "Hello Peter"
enhancedPromise.sayHello();
【讨论】:
【参考方案16】:最佳做法是使用安全输入,我建议您:
interface customObject extends MyObject
newProp: string;
newProp2: number;
【讨论】:
【参考方案17】:通过将其类型转换为“any”,将任何新属性存储在任何类型的对象上:
var extend = <any>myObject;
extend.NewProperty = anotherObject;
稍后您可以通过将扩展对象转换回“任何”来检索它:
var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;
【讨论】:
这完全是正确的解决方案。假设你有一个对象 let o : ObjectType; .... 稍后您可以将 o 转换为任何 (o).newProperty = 'foo';它可以像 (o).newProperty 一样检索。没有编译器错误,工作起来就像一个魅力。 这会阻碍智能感知......除了保持智能感知之外,有什么办法可以解决这个问题吗?【参考方案18】:要保留您以前的类型,请将您的对象临时转换为任何类型
var obj =
(<any>obj).prop = 5;
新的动态属性仅在您使用强制转换时可用:
var a = obj.prop; ==> Will generate a compiler error
var b = (<any>obj).prop; ==> Will assign 5 to b with no error;
【讨论】:
【参考方案19】:为 Angular 扩展 @jmvtrinidad 解决方案,
当使用已经存在的类型化对象时,这是添加新属性的方法。
let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
现在如果你想在 html 端使用otherProperty
,这就是你需要的:
<div *ngIf="$any(user).otherProperty">
...
...
</div>
Angular 编译器将$any()
视为any
类型的转换,就像在TypeScript 中使用<any>
或as any
转换时一样。
【讨论】:
【参考方案20】:在 TypeScript 中为对象动态分配属性。
要做到这一点,您只需要像这样使用 typescript 接口:
interface IValue
prop1: string;
prop2: string;
interface IType
[code: string]: IValue;
你可以这样使用它
var obj: IType = ;
obj['code1'] =
prop1: 'prop 1 value',
prop2: 'prop 2 value'
;
【讨论】:
我尝试使用您的代码,但没有收到任何错误:pastebin.com/NBvJifzN 尝试初始化SomeClass
中的 attributes
字段,这应该可以修复它 public attributes: IType = ;
pastebin.com/3xnu0TnN【参考方案21】:
您可以添加此声明以使警告静音。
declare var obj: any;
【讨论】:
【参考方案22】:如果你使用的是 Typescript,想必你想使用类型安全;在这种情况下,裸对象和“任何”都被禁止。
最好不要使用 Object 或 ,而是使用一些命名类型;或者您可能正在使用具有特定类型的 API,您需要使用自己的字段进行扩展。我发现这行得通:
class Given ... // API specified fields; or maybe it's just Object
interface PropAble extends Given
props?: string; // you can cast any Given to this and set .props
// '?' indicates that the field is optional
let g:Given = getTheGivenObject();
(g as PropAble).props = "value for my new field";
// to avoid constantly casting:
let k:PropAble = getTheGivenObject();
k.props = "value for props";
【讨论】:
【参考方案23】:试试这个:
export interface QueryParams
page?: number,
limit?: number,
name?: string,
sort?: string,
direction?: string
那就用吧
const query =
name: 'abc'
query.page = 1
【讨论】:
【参考方案24】:唯一完全类型安全的解决方案是this one,但有点罗嗦,会强制您创建多个对象。
如果您必须首先创建一个空对象,然后选择这两种解决方案之一。请记住,每次使用 as
,您都会失去安全感。
更安全的解决方案
object
的类型在getObject
中是safe,这意味着object.a
的类型将是string | undefined
interface Example
a: string;
b: number;
function getObject()
const object: Partial<Example> = ;
object.a = 'one';
object.b = 1;
return object as Example;
短解决方案
object
的类型在getObject
中是不安全,这意味着object.a
甚至在分配之前就是string
类型。
interface Example
a: string;
b: number;
function getObject()
const object = as Example;
object.a = 'one';
object.b = 1;
return object;
【讨论】:
以上是关于如何在 TypeScript 中为对象动态分配属性?的主要内容,如果未能解决你的问题,请参考以下文章