Bagaimana cara menetapkan properti secara dinamis ke objek dalam TypeScript?

359

Jika saya ingin secara sistematis menetapkan properti ke objek dalam Javascript, saya akan melakukannya seperti ini:

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

Namun dalam TypeScript, ini menghasilkan kesalahan:

Properti 'prop' tidak ada pada nilai tipe '{}'

Bagaimana saya bisa menetapkan properti baru ke objek di TypeScript?

Peter Olson
sumber
1
FYI. Saya telah membuka diskusi terkait hal ini jika Anda ingin mengikutinya.
joshuapoehls

Jawaban:

456

Dimungkinkan untuk dilambangkan objsebagai any, tetapi itu mengalahkan seluruh tujuan menggunakan naskah. obj = {}menyiratkan objadalah Object. Menandainya sebagai anytidak masuk akal. Untuk mencapai konsistensi yang diinginkan, sebuah antarmuka dapat didefinisikan sebagai berikut.

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

ATAU untuk membuatnya kompak:

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

LooseObjectdapat menerima bidang dengan string apa pun sebagai kunci dan anyketik sebagai nilai.

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

Keanggunan nyata dari solusi ini adalah Anda bisa memasukkan bidang-bidang typesafe di antarmuka.

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;

Meskipun ini menjawab pertanyaan Asli, jawaban di sini oleh @GreeneCreations mungkin memberikan perspektif lain tentang cara mendekati masalah.

Akash Kurian Jose
sumber
13
Saya pikir ini adalah solusi terbaik sekarang. Saya pikir pada saat pertanyaan itu diajukan sifat indeks seperti ini belum diimplementasikan dalam TypeScript.
Peter Olson
bagaimana dengan objek asli seperti Kesalahan
Wayou
@Wayou objek asli seperti Error dan Array mewarisi dari Object. Jadi LooseObjectbisa mengambil Kesalahan juga.
Akash Kurian Jose
Bisakah Anda memeriksa apakah objek memiliki properti (dinamis) tertentu?
phhbr
1
untuk benda-benda longgar ini, bagaimana Anda menulisnya dalam bentuk pengambil-penyetel?
JokingBatman
80

Atau semuanya:

  var obj:any = {}
  obj.prop = 5;
Crwth
sumber
42
Apa gunanya TypeScript jika saya harus melemparkan begitu banyak hal anyuntuk menggunakannya? Hanya menjadi suara tambahan dalam kode saya ..: /
AjaxLeung
16
@AjaxLeung Jika Anda perlu melakukan itu, Anda salah menggunakannya.
Alex
2
Lihat edit tambahan di atas yang mempertahankan objek tipe sebelumnya.
Andre Vianna
6
@AjaxLeung Anda harus sangat jarang melakukan cast any. TypeScript digunakan untuk menangkap (potensi) kesalahan pada waktu kompilasi. Jika Anda menggunakan untuk anymembisukan kesalahan maka Anda kehilangan kekuatan mengetik dan mungkin juga kembali ke JS murni. anyidealnya hanya digunakan jika Anda mengimpor kode yang Anda tidak dapat menulis definisi TS atau saat memigrasi kode Anda dari JS ke TS
Precastic
63

Saya cenderung untuk menempatkan anydi sisi lain yaitu var foo:IFoo = <any>{};Jadi sesuatu seperti ini masih aman:

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; 

Atau Anda dapat menandai properti ini sebagai opsional:

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

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

Cobalah online

basarat
sumber
5
+1 untuk menjadi satu-satunya solusi yang menjaga keamanan tipe. Pastikan Anda menginstal semua properti non-opsional langsung setelahnya, untuk menghindari bug yang menggigit Anda di kemudian hari.
Aidiakapi
Apakah ini berhasil? Setelah kompilasi saya masih memiliki <any> {} di javascript saya.
bvs
4
I still have <any>{maka Anda belum dikompilasi. TypeScript akan menghapusnya dalam
emit
4
Hari-hari ini, var foo: IFoo = {} as anylebih disukai. Sintaksis yang lama untuk typecasting bertabrakan dengan TSX (Typescript-ified JSX).
Don
51

Solusi ini berguna ketika objek Anda memiliki Tipe Khusus. Seperti saat mendapatkan objek ke sumber lain.

let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
jmvtrinidad
sumber
7
Ini adalah solusi yang tepat untuk penugasan satu kali.
soundly_typed
Jawaban ini berhasil bagi saya karena untuk login facebook saya harus menambahkan properti ke objek window . Penggunaan pertama saya sebagai kata kunci di TypeScript.
AlanObject
33

Meskipun kompiler mengeluh itu masih harus menampilkannya seperti yang Anda butuhkan. Namun, ini akan berhasil.

var s = {};
s['prop'] = true;
Angelo R.
sumber
2
ya baik, ini bukan jenis script cara, Anda kehilangan intellisense.
pregmatch
26

Satu lagi opsi yang dapat dilakukan adalah mengakses properti sebagai koleksi:

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

Aviw
sumber
2
Ini adalah cara yang paling ringkas. Object.assign(obj, {prop: "value"})dari ES6 / ES2015 berfungsi juga.
Charles
9

Saya terkejut bahwa tidak ada jawaban referensi Object.assign karena itulah teknik yang saya gunakan setiap kali saya berpikir tentang "komposisi" dalam JavaScript.

Dan itu berfungsi seperti yang diharapkan dalam TypeScript:

interface IExisting {
    userName: string
}

interface INewStuff {
    email: string
}

const existingObject: IExisting = {
    userName: "jsmith"
}

const objectWithAllProps: IExisting & INewStuff = Object.assign({}, existingObject, {
    email: "[email protected]"
})

console.log(objectWithAllProps.email); // [email protected]

Keuntungan

  • ketik keamanan keseluruhan karena Anda tidak perlu menggunakan anytipe sama sekali
  • menggunakan tipe agregat TypeScript (seperti yang dilambangkan dengan &ketika mendeklarasikan tipe objectWithAllProps), yang dengan jelas mengomunikasikan bahwa kami sedang menyusun tipe baru sambil jalan (yaitu secara dinamis)

Hal-hal yang harus diperhatikan

  1. Object.assign memiliki aspek uniknya sendiri (yang terkenal dengan JS devs paling berpengalaman) yang harus dipertimbangkan saat menulis TypeScript.
    • Ini dapat digunakan dengan cara yang bisa berubah, atau dengan cara yang tidak berubah (saya menunjukkan cara abadi di atas, yang berarti existingObjecttetap tidak tersentuh dan karena itu tidak memiliki emailproperti. Bagi kebanyakan programmer bergaya fungsional, itu adalah hal yang baik karena hasilnya adalah satu-satunya perubahan baru).
    • Object.assign bekerja paling baik ketika Anda memiliki objek yang lebih datar. Jika Anda menggabungkan dua objek bersarang yang berisi properti yang dapat dibatalkan, Anda bisa menimpa nilai kebenaran dengan undefined. Jika Anda memperhatikan urutan argumen Object.assign, Anda harus baik-baik saja.
GreeneCreations
sumber
Bagi saya, dalam kasus di mana Anda perlu menggunakan ini untuk menampilkan data tampaknya berfungsi dengan baik, tetapi ketika Anda perlu menambahkan entri tipe yang dimodifikasi ke array, ini sepertinya tidak berfungsi dengan baik. Saya memilih untuk secara dinamis menyusun objek dan menetapkannya dalam satu baris, kemudian menetapkan properti dinamis dalam garis berturut-turut. Ini membuat saya hampir sampai ke sana, jadi terima kasih.
Shafiq Jetha
8

Anda dapat membuat objek baru berdasarkan objek lama menggunakan operator spread

interface MyObject {
    prop1: string;
}

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

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

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

TypeScript akan menyimpulkan semua bidang objek asli dan VSCode akan melakukan pelengkapan otomatis, dll.

Alex
sumber
bagus, tetapi jika Anda perlu menggunakan prop2 di misalnya prop3, akan sulit untuk diterapkan
ya_dimon
6

Sederhana akan mengikuti

const obj = <any>{};
obj.prop1 = "value";
obj.prop2 = "another value"
Jayant Varshney
sumber
5

Dimungkinkan untuk menambahkan anggota ke objek yang ada dengan

  1. pelebaran jenis (baca: perpanjang / spesialis antarmuka)
  2. melemparkan objek asli ke tipe yang diperluas
  3. tambahkan anggota ke objek
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();
Daniel Dietrich
sumber
1
solusi terbaik untuk saya. Saya telah belajar sesuatu yang baru hari ini, terima kasih
Maurice
saya mendapatkan kesalahan TS2352 dengan kode ini
ya_dimon
@ya_dimon mencobanya di TypeScript Playground: typescriptlang.org/play Berhasil !
Daniel Dietrich
@DanielDietrich sekarang berfungsi .. thx, tidak tahu apa yang terjadi.
ya_dimon
sama sama! Pergeseran terjadi;)
Daniel Dietrich
5

Karena Anda tidak dapat melakukan ini:

obj.prop = 'value';

Jika kompiler TS dan linter Anda tidak membatasi Anda, Anda dapat menulis ini:

obj['prop'] = 'value';

Jika kompiler atau linter TS Anda ketat, jawaban lain adalah mengetikkan:

var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";
LEMUEL ADANE
sumber
3
Ini jika 'noImplicitAny: false' di tsconfig.json
LEMUEL ADANE
Jika tidak, Anda dapat melakukan jawaban GreeneCreations.
LEMUEL ADANE
4

Simpan semua properti baru di objek apa pun dengan mengetikkannya ke 'apa saja':

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

Kemudian Anda dapat mengambilnya dengan melemparkan kembali objek Anda yang diperluas ke 'apa saja':

var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;
Erlend Robaye
sumber
Ini benar-benar solusi yang tepat. Katakanlah Anda memiliki objek, biarkan o: ObjectType; .... nanti Anda dapat memberikan o ke sembarang (<any> o) .newProperty = 'foo'; dan itu dapat diambil seperti (<any> o) .newProperty. Tidak ada kesalahan kompiler dan berfungsi seperti mantra.
Matt Ashley
Intellisense rem ini ... adakah cara untuk ini selain mempertahankan intellisense?
pregmatch
4

Untuk menjamin bahwa jenisnya adalah Object(yaitu pasangan nilai kunci ), gunakan:

const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'
bersling
sumber
4
  • Kasus 1:

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

  • Kasus 2:

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

ikarayel
sumber
4

Anda bisa menggunakan ini:

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

Anda dapat menambahkan deklarasi ini untuk membungkam peringatan.

declare var obj: any;

joshuapoehl
sumber
3

Praktik terbaik adalah menggunakan pengetikan yang aman, saya sarankan Anda:

interface customObject extends MyObject {
   newProp: string;
   newProp2: number;
}
Isidro Martínez
sumber
3

Untuk mempertahankan jenis Anda sebelumnya, tempelkan sementara objek Anda ke sembarang

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

Properti dinamis baru hanya akan tersedia ketika Anda menggunakan para pemain:

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

Ini adalah versi khusus Object.assign, yang secara otomatis menyesuaikan tipe variabel dengan setiap perubahan properti. Tidak perlu untuk variabel tambahan, jenis pernyataan, tipe eksplisit atau salinan objek:

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 }

Catatan: Sampel menggunakan fungsi penegasan TS 3.7 . Jenis kembalinya assignadalah void, tidak seperti Object.assign.

ford04
sumber
1

Jika Anda menggunakan naskah, mungkin Anda ingin menggunakan keamanan jenis; dalam hal ini Obyek telanjang dan 'apa saja' adalah kontra indikasi

Lebih baik tidak menggunakan Objek atau {}, tetapi beberapa jenis bernama; atau Anda mungkin menggunakan API dengan tipe tertentu, yang perlu diperluas dengan bidang Anda sendiri. Saya menemukan ini berfungsi:

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";
Jack Punt
sumber
1

menetapkan properti secara dinamis ke objek dalam TypeScript.

untuk melakukannya, Anda hanya perlu menggunakan antarmuka naskah seperti:

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

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

Anda bisa menggunakannya seperti itu

var obj: IType = {};
obj['code1'] = { 
    prop1: 'prop 1 value', 
    prop2: 'prop 2 value' 
};
Nerdroid
sumber
1
Saya mencoba dengan kode Anda, tetapi saya tidak menerima kesalahan apa pun: pastebin.com/NBvJifzN
pablorsk
1
coba inisialisasi attributesbidang di dalam SomeClass dan itu harus memperbaikinya public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid
1

Coba ini:

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

Lalu gunakan itu

const query = {
    name: 'abc'
}
query.page = 1
Tung Nguyen
sumber
0

Satu-satunya solusi yang sepenuhnya aman untuk tipe adalah yang ini , tetapi sedikit bertele-tele dan memaksa Anda untuk membuat beberapa objek.

Jika Anda harus membuat objek kosong terlebih dahulu, pilih salah satu dari dua solusi ini. Ingatlah bahwa setiap kali Anda menggunakan as, Anda kehilangan keselamatan.

Solusi yang lebih aman

Jenis objectini aman di dalam getObject, yang berarti object.aakan jenisstring | undefined

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

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

Solusi singkat

Jenis objectini tidak aman di dalam getObject, yang berarti object.aakan jenis stringbahkan sebelum tugas nya.

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

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
fregante
sumber