Jak dynamicznie przypisywać właściwości do obiektu w TypeScript?


359

Gdybym chciał programowo przypisać właściwość do obiektu w JavaScript, zrobiłbym to w ten sposób:

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

Ale w TypeScript powoduje to błąd:

Właściwość „prop” nie istnieje w przypadku wartości typu „{}”

Jak mam przypisać nową właściwość do obiektu w TypeScript?


1
Do Twojej wiadomości Mam otworzył dyskusję związaną z tym, jeśli chcesz, aby ją śledzić.
joshuapoehls

Odpowiedzi:


454

Można to określić objjako any, ale to przeczy celowi używania maszynopisu. obj = {}oznacza, że objjest Object. Oznaczenie go jako anynie ma sensu. Aby osiągnąć pożądaną spójność, interfejs można zdefiniować w następujący sposób.

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

LUB, aby uczynić go kompaktowym:

var obj: {[k: string]: any} = {};

LooseObjectmoże akceptować pola z dowolnym ciągiem jako kluczem i anywpisać jako wartość.

obj.prop = "value";
obj.prop2 = 88;

Prawdziwą elegancją tego rozwiązania jest to, że możesz zawrzeć w interfejsie bezpieczne pola.

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;

Chociaż odpowiada to oryginalne pytanie, odpowiedź tutaj @GreeneCreations może dać inną perspektywę, jak podejść do problemu.


13
Myślę, że teraz jest to najlepsze rozwiązanie. Myślę, że w chwili, gdy pytanie zostało zadane, takie właściwości indeksu nie zostały jeszcze zaimplementowane w TypeScript.
Peter Olson,

co z rodzimym obiektem, takim jak Error
Wayou

@Wayou rodzime obiekty, takie jak Error i Array, dziedziczą po Object. Więc LooseObjectmoże również wziąć błąd.
Akash Kurian Jose

Czy możesz sprawdzić, czy obiekt ma określoną (dynamiczną) właściwość?
phhbr

1
w przypadku tych luźnych obiektów, w jaki sposób piszesz je w formie ustawiacza?
JokingBatman

81

Lub wszystko za jednym razem:

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

41
Jaki jest sens TypeScript, jeśli muszę anyużyć tak wielu rzeczy, aby z niego skorzystać? Staje się dodatkowy szum w moim kodzie ..: /
AjaxLeung

16
@AjaxLeung Jeśli musisz to zrobić, używasz go źle.
Alex

2
Zobacz dodatkową edycję powyżej, która zachowuje poprzedni typ obiektu.
Andre Vianna,

6
@AjaxLeung Powinieneś bardzo rzadko przesyłać na any. TypeScript służy do wychwytywania (potencjalnych) błędów w czasie kompilacji. Jeśli użyjesz opcji anywyciszania błędów, tracisz moc pisania i równie dobrze możesz wrócić do czystej wersji JS. anynajlepiej używać tylko wtedy, gdy importujesz kod, dla którego nie możesz pisać definicji TS lub migrujesz swój kod z JS do TS
Precastic

63

Zwykle stawiam anyna drugą stronę, var foo:IFoo = <any>{};więc coś takiego jest nadal bezpieczne:

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; 

Alternatywnie możesz oznaczyć te właściwości jako opcjonalne:

interface IFoo{
    bar?:string;
    baz?:string;
    boo?:string;    
}

// Now your simple initialization works
var foo:IFoo = {};

Wypróbuj online


5
+1 za to, że jest jedynym rozwiązaniem zapewniającym bezpieczeństwo typu. Po prostu upewnij się, że instytujesz wszystkie nie opcjonalne właściwości bezpośrednio po nim, aby uniknąć późniejszych błędów.
Aidiakapi

Czy to naprawdę działa? Po kompilacji nadal mam <any> {} w moim javascript.
ur.

4
I still have <any>{to nie skompilowałeś. TypeScript
usunąłby

4
Te dni var foo: IFoo = {} as anysą preferowane. Stara składnia do rzutowania typów kolidowała z TSX (JSX z ifescript-ified).
Don

51

To rozwiązanie jest przydatne, gdy obiekt ma określony typ. Podobnie jak przy uzyskiwaniu obiektu do innego źródła.

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

7
To jest właściwe rozwiązanie dla zadań jednorazowych.
soundly_typed 30.01.17

Ta odpowiedź działała dla mnie, ponieważ do logowania na Facebooku musiałem dodać właściwości do obiektu okna . Moje pierwsze użycie słowa kluczowego as w TypeScript.
AlanObject

33

Chociaż kompilator narzeka, powinien nadal wyświetlać go zgodnie z wymaganiami. Jednak to zadziała.

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

2
tak cóż, to nie jest tak naprawdę sposób pisania skryptów, tracisz inteligencję.
przedpremierowy

25

Jeszcze jedną opcją jest dostęp do właściwości jako kolekcji:

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


2
To jest najbardziej zwięzły sposób. Object.assign(obj, {prop: "value"})z ES6 / ES2015 również działa.
Charles

8

Dziwi mnie, że żadna z odpowiedzi nie odwołuje się do Object.assign, ponieważ tej techniki używam za każdym razem, gdy myślę o „kompozycji” w JavaScript.

I działa zgodnie z oczekiwaniami w 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

Zalety

  • wpisz bezpieczeństwo, ponieważ nie musisz anyw ogóle używać tego typu
  • używa typu agregacyjnego TypeScript (jak określa to &przy deklarowaniu typu objectWithAllProps), co wyraźnie informuje, że komponujemy nowy typ w locie (tj. dynamicznie)

Rzeczy, o których należy pamiętać

  1. Object.assign ma swoje unikalne aspekty (dobrze znane większości doświadczonych programistów JS), które należy wziąć pod uwagę podczas pisania TypeScript.
    • Można go używać w sposób zmienny lub niezmienny (powyżej pokazuję niezmienny sposób, co oznacza, że existingObjectpozostaje nietknięty, a zatem nie ma emailwłaściwości. Dla większości programistów w stylu funkcjonalnym jest to dobra rzecz, ponieważ wynikiem jest jedyna nowa zmiana).
    • Object.assign działa najlepiej, gdy masz bardziej płaskie obiekty. Jeśli łączysz dwa zagnieżdżone obiekty, które zawierają właściwości zerowalne, możesz w końcu nadpisać prawdziwe wartości niezdefiniowanym. Jeśli uważasz na kolejność argumentów Object.assign, wszystko powinno być w porządku.

Dla mnie, w przypadkach, w których musisz użyć tego do wyświetlania danych, wydaje się, że działa dobrze, ale kiedy musisz dodać wpisy do zmodyfikowanych typów do tablicy, wydaje się, że nie działa to zbyt dobrze. Zacząłem dynamicznie komponować obiekt i przypisywać go w jednej linii, a następnie przypisywać właściwości dynamiczne w kolejnych liniach. To doprowadziło mnie do końca, więc dziękuję.
Shafiq Jetha

8

Możesz utworzyć nowy obiekt na podstawie starego obiektu za pomocą operatora rozkładania

interface MyObject {
    prop1: string;
}

const myObj: MyObject = {
    prop1: 'foo',
}

const newObj = {
    ...myObj,
    prop2: 'bar',
}

console.log(newObj.prop2); // 'bar'

TypeScript wywnioskuje wszystkie pola oryginalnego obiektu, a VSCode wykona autouzupełnianie itp.


fajnie, ale jeśli będziesz musiał użyć prop2 np. prop3, będzie trudny do wdrożenia
ya_dimon

6

Najprostsze będą następujące

const obj = <any>{};
obj.prop1 = "value";
obj.prop2 = "another value"

5

Możliwe jest dodanie członka do istniejącego obiektu przez

  1. poszerzenie typu (czytaj: rozszerz / specjalizuj interfejs)
  2. rzutuj oryginalny obiekt na typ rozszerzony
  3. dodaj członka do obiektu
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
najlepsze rozwiązanie dla mnie. Nauczyłem się dzisiaj czegoś nowego, dziękuję
Maurice

Otrzymuję błąd TS2352 z tym kodem
ya_dimon

@ya_dimon spróbować go na maszynopis Playground: typescriptlang.org/play działa dobrze!
Daniel Dietrich

@DanielDietrich teraz działa .. dzięki, nie mam pojęcia, co się stało.
ya_dimon

Zapraszamy! zmiana się dzieje;)
Daniel Dietrich

5

Ponieważ nie możesz tego zrobić:

obj.prop = 'value';

Jeśli twój kompilator TS i twój linter Cię nie ograniczają, możesz napisać to:

obj['prop'] = 'value';

Jeśli Twój kompilator lub linter TS jest ścisły, inną odpowiedzią byłoby typecast:

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

3
Dzieje się tak, jeśli „noImplicitAny: false” w pliku tsconfig.json
LEMUEL ADANE

W przeciwnym razie możesz zrobić odpowiedź GreeneCreations.
LEMUEL ADANE

4

Przechowuj każdą nową właściwość na dowolnym rodzaju obiektu, przypisując ją do „any”:

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

Później możesz go odzyskać, rzutując swój rozszerzony obiekt z powrotem na „dowolny”:

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

To jest całkowicie poprawne rozwiązanie. Powiedzmy, że masz obiekt let o: ObjectType; .... później możesz przesłać o na dowolny (<any> o) .newProperty = 'foo'; i można go pobrać jak (<any> o) .newProperty. Brak błędów kompilatora i działa jak urok.
Matt Ashley,

to hamuje intellisense ... czy jest na to jakikolwiek sposób, ale zachować intellisense?
przedpremierowy

4

Aby zagwarantować, że typ jest Object(tj. Pary klucz-wartość ), użyj:

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

4
  • Przypadek 1:

    var car = {type: "BMW", model: "i8", color: "white"}; car['owner'] = "ibrahim"; // You can add a property:

  • Przypadek 2:

    var car:any = {type: "BMW", model: "i8", color: "white"}; car.owner = "ibrahim"; // You can set a property: use any type


4

możesz użyć tego:

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


3

Najlepszą praktyką jest bezpieczne pisanie, polecam:

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

3

Aby zachować poprzedni typ, tymczasowo rzutuj obiekt na dowolny

  var obj = {}
  (<any>obj).prop = 5;

Nowa właściwość dynamiczna będzie dostępna tylko podczas używania rzutowania:

  var a = obj.prop; ==> Will generate a compiler error
  var b = (<any>obj).prop; ==> Will assign 5 to b with no error;

3

Oto specjalna wersja Object.assign, która automatycznie dostosowuje typ zmiennej przy każdej zmianie właściwości. Nie ma potrzeby stosowania dodatkowych zmiennych, asercji typów, typów jawnych ani kopii obiektów:

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 }

Uwaga: Próbka wykorzystuje funkcje asercji TS 3.7 . Zwrot typu assignjest voidw przeciwieństwie do Object.assign.


1

Jeśli używasz maszynopisu, prawdopodobnie chcesz używać bezpieczeństwa typu; w takim przypadku nagi Obiekt i „dowolny” są przeciwwskazane.

Lepiej nie używać Object ani {}, ale niektóre nazwane typy; lub możesz używać interfejsu API z określonymi typami, które musisz rozszerzyć o własne pola. Znalazłem to do pracy:

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

dynamicznie przypisuj właściwości do obiektu w TypeScript.

Aby to zrobić, wystarczy użyć interfejsów maszynopisu w taki sposób:

interface IValue {
    prop1: string;
    prop2: string;
}

interface IType {
    [code: string]: IValue;
}

możesz tego tak używać

var obj: IType = {};
obj['code1'] = { 
    prop1: 'prop 1 value', 
    prop2: 'prop 2 value' 
};

1
Próbuję z twoim kodem, ale nie otrzymuję żadnego błędu: pastebin.com/NBvJifzN
pablorsk

1
spróbuj zainicjować attributespole wewnątrz, SomeClass które powinno to naprawić public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid

1

Spróbuj tego:

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

Następnie użyj go

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

0

Jedynym rozwiązaniem, które jest w pełni bezpieczne dla typu, jest to , ale jest trochę niewygodne i zmusza cię do tworzenia wielu obiektów.

Jeśli musisz najpierw utworzyć pusty obiekt, wybierz jedno z tych dwóch rozwiązań. Pamiętaj, że za każdym razem astracisz bezpieczeństwo.

Bezpieczniejsze rozwiązanie

Rodzaj wewnątrz objectjest bezpiecznygetObject , co oznacza, object.aże będzie tego typustring | undefined

interface Example {
  a: string;
  b: number;
}

function getObject() {
  const object: Partial<Example> = {};
  object.a = 'one';
  object.b = 1;
  return object as Example;
}

Krótkie rozwiązanie

Rodzaj wewnątrz nieobject jest bezpiecznygetObject , co oznacza, że object.abędzie taki sam, stringzanim jeszcze zostanie przypisany.

interface Example {
  a: string;
  b: number;
}

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.