Как динамически назначать свойства объекту в TypeScript?


359

Если бы я хотел программно назначить свойство для объекта в Javascript, я бы сделал это так:

var obj = {};
obj.prop = "value";

Но в TypeScript это вызывает ошибку:

Свойство 'prop' не существует для значения типа '{}'

Как я должен назначить любое новое свойство для объекта в TypeScript?


1
FYI. Я открыл дискуссию, связанную с этим, если вы хотите следовать ей.
joshuapoehls

Ответы:


456

Это можно обозначить 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;

Хотя это отвечает на оригинальный вопрос, ответ здесь @GreeneCreations может дать другую точку зрения на то, как подойти к проблеме.


13
Я думаю, что это лучшее решение сейчас. Я думаю, что в то время, когда задавался вопрос, свойства индекса, подобные этому, еще не были реализованы в TypeScript.
Питер Олсон

как насчет нативного объекта, такого как Ошибка
Wayou

@ Ваши собственные объекты, такие как Error и Array, наследуются от Object. Так же LooseObjectможет принять ошибку.
Акаш Куриан Хосе

Можете ли вы проверить, имеет ли объект определенное (динамическое) свойство?
phhbr

1
для этих свободных объектов, как вы пишете их в форме getter-setter?
JokingBatman

80

Или все за один раз:

  var obj:any = {}
  obj.prop = 5;

42
Какой смысл в TypeScript, если мне нужно разыграть так много вещей any, чтобы использовать его? Просто становится лишним шумом в моем коде ..: /
AjaxLeung

16
@AjaxLeung Если вам нужно сделать это, вы используете это неправильно.
Алекс

2
Смотрите дополнительное редактирование выше, которое сохраняет предыдущий тип объекта.
Андре Вианна

6
@AjaxLeung Вы должны очень редко бросать на any. TypeScript используется для отлова (потенциальных) ошибок во время компиляции. Если вы преобразуете в anyсообщения об ошибках, вы теряете способность печатать и можете вернуться к чистому JS. anyв идеале следует использовать, только если вы импортируете код, для которого вы не можете написать определения TS, или во время переноса кода из JS в TS
Precastic

63

Я склонен ставить 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 = {};

Попробуйте онлайн


5
+1 за единственное решение, обеспечивающее безопасность типов. Просто убедитесь, что вы создаете все не необязательные свойства сразу после него, чтобы избежать ошибок, кусающих вас позже.
Aidiakapi

Это на самом деле работает? После компиляции у меня все еще есть <any> {} в моем JavaScript.
15:15

4
I still have <any>{тогда вы не скомпилировали. TypeScript удалил бы это в своем излучении
basarat

4
В эти дни var foo: IFoo = {} as anyпредпочтительнее. Старый синтаксис для Typecasting сталкивался с TSX (Typescript-ified JSX).
Дон

51

Это решение полезно, когда ваш объект имеет определенный тип. Как при получении объекта в другой источник.

let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.

7
Это правильное решение для одноразовых заданий.
soundly_typed

Этот ответ работал для меня, потому что для входа в Facebook мне пришлось добавить свойства к объекту окна . Мое первое использование ключевого слова as в TypeScript.
AlanObject

33

Хотя компилятор жалуется, он все равно должен выводить его так, как вам нужно. Тем не менее, это будет работать.

var s = {};
s['prop'] = true;

2
да, ну, это не совсем сценарий типа, вы теряете intellisense.
pregmatch

26

Еще один способ сделать это - получить доступ к свойству как к коллекции:

var obj = {};
obj['prop'] = "value";


2
Это самый лаконичный способ. Object.assign(obj, {prop: "value"})от ES6 / ES2015 тоже работает.
Чарльз

9

Я удивлен, что ни один из ответов не ссылается на 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), который четко сообщает, что мы создаем новый тип на лету (то есть динамически)

Что нужно знать

  1. Object.assign имеет свои уникальные аспекты (которые хорошо известны большинству опытных разработчиков JS), которые следует учитывать при написании TypeScript.
    • Он может быть изменяемым или неизменным (я демонстрирую неизменный способ выше, что означает, что он existingObjectостается неизменным и, следовательно, не имеет emailсвойства. Для большинства программистов в функциональном стиле это хорошо, так как результат единственное новое изменение).
    • Object.assign работает лучше всего, когда у вас есть более плоские объекты. Если вы объединяете два вложенных объекта, которые содержат обнуляемые свойства, вы можете перезаписать истинные значения неопределенными. Если вы следите за порядком аргументов 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, будет сложно реализовать
ya_dimon


5

Можно добавить члена к существующему объекту

  1. расширение типа (читай: расширять / специализировать интерфейс)
  2. приведите исходный объект к расширенному типу
  3. добавить член к объекту
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();

1
лучшее решение для меня. Сегодня я узнал что-то новое, спасибо
Морис

я получаю TS2352 ошибку с этим кодом
ya_dimon

@ya_dimon попробуйте на TypeScript Playground: typescriptlang.org/play Это отлично работает!
Даниэль Дитрих

@DanielDietrich теперь работает .. спасибо, не знаю, что случилось.
ya_dimon

пожалуйста! сдвиг происходит;)
Даниэль Дитрих

5

Поскольку вы не можете сделать это:

obj.prop = 'value';

Если ваш компилятор TS и ваш линтер не строгие к вам, вы можете написать это:

obj['prop'] = 'value';

Если ваш компилятор TS или линтер строгий, другой ответ был бы для typecast:

var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";

3
Это если 'noImplicitAny: false' на tsconfig.json
LEMUEL ADANE

Еще вы можете сделать GreeneCreations ответ.
LEMUEL ADANE

4

Сохраните любое новое свойство на объекте любого типа, указав его тип «any»:

var extend = <any>myObject;
extend.NewProperty = anotherObject;

Позже вы можете получить его, переведя расширенный объект обратно в 'any':

var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;

Это абсолютно правильное решение. Допустим, у вас есть объект let o: ObjectType; .... позже вы можете привести o к любому (<any> o) .newProperty = 'foo'; и его можно получить как (<any> o) .newProperty. Нет ошибок компилятора и работает как шарм.
Мэтт Эшли

это тормозит intellisense ... есть ли способ это сделать, кроме как сохранить intellisense?
pregmatch

4

Чтобы гарантировать, что тип является Object(т.е. пары ключ-значение ), используйте:

const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'

4
  • Дело 1:

    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


4

Вы можете использовать это:

this.model = Object.assign(this.model, { newProp: 0 });

3

Вы можете добавить эту декларацию, чтобы заставить замолчать предупреждения.

declare var obj: any;


3

Лучше всего использовать безопасную печать, я рекомендую вам:

interface customObject extends MyObject {
   newProp: string;
   newProp2: number;
}

3

Чтобы сохранить ваш предыдущий тип, временно приведите ваш объект к любому

  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;

3

Вот специальная версия 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 }

Примечание. В примере используются функции утверждений TS 3.7 . Возвращаемый тип assignявляется void, в отличие от Object.assign.


1

Если вы используете 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";

1

динамически назначать свойства объекту в 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' 
};

1
Я пытаюсь с вашим кодом, но я не получаю никакой ошибки: pastebin.com/NBvJifzN
pablorsk

1
попробуйте инициализировать attributesполе внутри, SomeClass и это должно исправить. public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid

1

Попробуй это:

export interface QueryParams {
    page?: number,
    limit?: number,
    name?: string,
    sort?: string,
    direction?: string
}

Тогда используйте это

const query = {
    name: 'abc'
}
query.page = 1

0

Единственное решение , которое полностью типобезопасный является это один , но немного многословным и сила создавать несколько объектов.

Если вам необходимо сначала создать пустой объект, выберите одно из этих двух решений. Имейте в виду, что каждый раз, когда вы используете as, вы теряете безопасность.

Более безопасное решение

Тип objectявляется безопасным внутри getObject, что средства 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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.