Làm cách nào để tôi tự động gán các thuộc tính cho một đối tượng trong TypeScript?


359

Nếu tôi muốn lập trình gán một thuộc tính cho một đối tượng trong Javascript, tôi sẽ làm như thế này:

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

Nhưng trong TypeScript, điều này tạo ra một lỗi:

Thuộc tính 'prop' không tồn tại trên giá trị của loại '{}'

Làm thế nào tôi có thể gán bất kỳ thuộc tính mới nào cho một đối tượng trong TypeScript?


1
FYI. Tôi đã mở một cuộc thảo luận liên quan đến vấn đề này nếu bạn muốn theo dõi nó.
joshuapoehls

Câu trả lời:


456

Có thể biểu thị objany, nhưng điều đó đánh bại toàn bộ mục đích sử dụng bản thảo. obj = {}ngụ ý objlà một Object. Đánh dấu nó là anykhông có ý nghĩa. Để thực hiện tính nhất quán mong muốn, một giao diện có thể được xác định như sau.

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

HOẶC để làm cho nó nhỏ gọn:

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

LooseObjectcó thể chấp nhận các trường với bất kỳ chuỗi nào là khóa và anynhập dưới dạng giá trị.

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

Sự thanh lịch thực sự của giải pháp này là bạn có thể bao gồm các trường loại an toàn trong giao diện.

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;

Trong khi điều này trả lời câu hỏi Ban đầu, câu trả lời ở đây của @GreeneCreations có thể đưa ra một quan điểm khác về cách tiếp cận vấn đề.


13
Tôi nghĩ rằng đây là giải pháp tốt nhất bây giờ. Tôi nghĩ tại thời điểm câu hỏi được hỏi thuộc tính chỉ mục như thế này chưa được triển khai trong TypeScript.
Peter Olson

Làm thế nào về các đối tượng bản địa như Lỗi
Wayou

Các đối tượng gốc @Wayou như Error và Array kế thừa từ Object. Vì vậy, LooseObjectcó thể có một lỗi là tốt.
Akash Kurian Jose

Bạn có thể kiểm tra xem đối tượng có thuộc tính (động) nhất định không?
phhbr

1
Đối với những đối tượng lỏng lẻo này, làm thế nào để bạn viết chúng dưới dạng getter-setter?
JokingBatman

80

Hoặc tất cả trong một lần:

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

42
Quan điểm của TypeScript là gì nếu tôi phải sử dụng quá nhiều thứ anyđể sử dụng nó? Chỉ trở nên thêm tiếng ồn trong mã của tôi ..: /
AjaxLeung

16
@AjaxLeung Nếu bạn cần làm điều đó, bạn đang sử dụng sai.
Alex

2
Xem chỉnh sửa bổ sung ở trên mà bảo tồn loại đối tượng trước đó.
Andre Vianna

6
@AjaxLeung Bạn rất hiếm khi sử dụng any. TypeScript được sử dụng để bắt lỗi (tiềm năng) tại thời điểm biên dịch. Nếu bạn sử dụng để anytắt tiếng thì bạn sẽ mất khả năng gõ và cũng có thể quay lại JS thuần túy. anychỉ nên sử dụng lý tưởng nếu bạn đang nhập mã mà bạn không thể viết định nghĩa TS hoặc trong khi di chuyển mã của mình từ JS sang TS
Precastic

63

Tôi có xu hướng đặt anyở phía bên kia tức là var foo:IFoo = <any>{};một cái gì đó như thế này vẫn an toàn:

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; 

Ngoài ra, bạn có thể đánh dấu các thuộc tính này là tùy chọn:

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

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

Dùng thử trực tuyến


5
+1 vì là giải pháp duy nhất giữ an toàn cho loại. Chỉ cần đảm bảo rằng bạn cung cấp tất cả các thuộc tính không tùy chọn ngay sau nó, để tránh các lỗi cắn bạn sau này.
Aidiakapi

Điều này thực sự làm việc? Sau khi biên dịch, tôi vẫn còn <any> {} trong javascript của mình.
bvs

4
I still have <any>{sau đó bạn chưa biên dịch. TypeScript sẽ loại bỏ nó trong phần phát ra của nó
basarat

4
Những ngày này, var foo: IFoo = {} as anyđược ưa thích. Cú pháp cũ cho typecasting đã va chạm với TSX (Kiểu chữ-ified JSX).
Don

51

Giải pháp này rất hữu ích khi đối tượng của bạn có Loại cụ thể. Giống như khi lấy đối tượng sang nguồn khác.

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

7
Đây là giải pháp chính xác cho các bài tập một lần.
soundly_typed

Câu trả lời này có hiệu quả với tôi vì để đăng nhập facebook tôi phải thêm thuộc tính vào đối tượng cửa sổ . Lần đầu tiên tôi sử dụng từ khóa as trong TypeScript.
AlanObject

33

Mặc dù trình biên dịch phàn nàn nó vẫn nên xuất nó như bạn yêu cầu. Tuy nhiên, điều này sẽ làm việc.

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

2
vâng, đây không thực sự là kiểu kịch bản, bạn mất trí nhớ.
pregmatch

26

Thêm một lựa chọn nữa là truy cập vào tài sản dưới dạng bộ sưu tập:

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


2
Đây là cách ngắn gọn nhất. Object.assign(obj, {prop: "value"})từ ES6 / ES2015 cũng hoạt động.
Charles

9

Tôi ngạc nhiên khi không có câu trả lời nào tham khảo Object.assign vì đó là kỹ thuật tôi sử dụng bất cứ khi nào tôi nghĩ về "thành phần" trong JavaScript.

Và nó hoạt động như mong đợi trong 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

Ưu điểm

  • loại an toàn xuyên suốt vì bạn không cần sử dụng any loại này
  • sử dụng loại tổng hợp của TypeScript (như được biểu thị bằng &khi khai báo loại objectWithAllProps), thông báo rõ ràng rằng chúng tôi đang soạn thảo một loại mới đang hoạt động (tức là động)

Những điều cần chú ý

  1. Object.assign có các khía cạnh độc đáo của riêng nó (được biết đến với hầu hết các nhà phát triển JS có kinh nghiệm) nên được xem xét khi viết TypeScript.
    • Nó có thể được sử dụng theo cách biến đổi hoặc theo cách bất biến (tôi chứng minh cách bất biến ở trên, có nghĩa là existingObjectkhông bị ảnh hưởng và do đó không có emailtài sản. Đối với hầu hết các lập trình viên theo phong cách chức năng, đó là một điều tốt vì kết quả là sự thay đổi mới duy nhất).
    • Object.assign hoạt động tốt nhất khi bạn có các đối tượng phẳng hơn. Nếu bạn đang kết hợp hai đối tượng lồng nhau có chứa các thuộc tính nullable, bạn có thể ghi đè lên các giá trị trung thực không xác định. Nếu bạn coi chừng thứ tự của các đối số Object.assign, bạn sẽ ổn thôi.

Đối với tôi, trong trường hợp bạn cần sử dụng điều này để hiển thị dữ liệu thì nó có vẻ hoạt động tốt, nhưng khi bạn cần thêm các mục đã sửa đổi vào một mảng, điều này dường như không hoạt động tốt. Tôi đã dùng đến việc tự động soạn thảo đối tượng và gán nó trong một dòng, sau đó gán các thuộc tính động trong các dòng liên tiếp. Điều này đã cho tôi hầu hết các cách đó, vì vậy cảm ơn bạn.
Shafiq Jetha

8

Bạn có thể tạo đối tượng mới dựa trên đối tượng cũ bằng toán tử trải

interface MyObject {
    prop1: string;
}

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

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

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

TypeScript sẽ suy ra tất cả các trường của đối tượng ban đầu và VSCode sẽ tự động hoàn thành, v.v.


tốt, nhưng trong trường hợp bạn cần sử dụng prop2 trong ví dụ prop3, sẽ khó thực hiện
ya_dimon

6

Đơn giản nhất sẽ được theo sau

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

5

Có thể thêm một thành viên vào một đối tượng hiện có bằng cách

  1. mở rộng loại (đọc: mở rộng / chuyên về giao diện)
  2. truyền đối tượng ban đầu sang loại mở rộng
  3. thêm thành viên vào đối tượng
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
giải pháp tốt nhất cho tôi Tôi đã học được điều gì đó mới hôm nay, cảm ơn bạn
Maurice

tôi nhận được lỗi TS2352 với mã này
ya_dimon

@ya_dimon thử nó trên nguyên cảo Playground: typescriptlang.org/play Nó hoạt động tốt!
Daniel Dietrich

@DanielDietrich hiện hoạt động .. thx, không biết chuyện gì đã xảy ra.
ya_dimon

không có gì! sự thay đổi xảy ra;)
Daniel Dietrich

5

Vì bạn không thể làm điều này:

obj.prop = 'value';

Nếu trình biên dịch TS và kẻ nói dối của bạn không nghiêm khắc với bạn, bạn có thể viết điều này:

obj['prop'] = 'value';

Nếu trình biên dịch hoặc trình phân tích TS của bạn nghiêm ngặt, một câu trả lời khác sẽ là typecast:

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

3
Đây là nếu 'noImplicitAny: false' trên tsconfig.json
LEMUEL ADane

Khác, bạn có thể làm GreeneCreations trả lời.
LEMUEL ADane

4

Lưu trữ bất kỳ thuộc tính mới nào trên bất kỳ loại đối tượng nào bằng cách đánh máy nó thành 'bất kỳ':

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

Sau này, bạn có thể truy xuất nó bằng cách chuyển đối tượng mở rộng của mình trở lại 'any':

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

Đây hoàn toàn là giải pháp chính xác. Hãy nói rằng bạn có một đối tượng hãy o: ObjectType; .... sau này bạn có thể truyền o cho bất kỳ (<any> o) .newProperty = 'foo'; và nó có thể được truy xuất như (<any> o) .newProperty. Không có lỗi biên dịch và hoạt động như một nét duyên dáng.
Matt Ashley

phanh này intellisense ... có cách nào để làm điều này nhưng để giữ intellisense?
pregmatch

4

Để đảm bảo rằng loại là một Object(tức là cặp khóa-giá trị ), hãy sử dụng:

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

4
  • Trường hợp 1:

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

  • Trường hợp 2:

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


4

bạn có thể sử dụng cái này:

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

3

Bạn có thể thêm tuyên bố này để im lặng cảnh báo.

declare var obj: any;


3

Thực hành tốt nhất là sử dụng gõ an toàn, tôi khuyên bạn:

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

3

Để duy trì loại trước đó của bạn, tạm thời bỏ đối tượng của bạn vào bất kỳ

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

Thuộc tính động mới sẽ chỉ khả dụng khi bạn sử dụng diễn viên:

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

3

Đây là một phiên bản đặc biệt Object.assign, tự động điều chỉnh loại biến với mỗi thay đổi thuộc tính. Không cần thêm biến, xác nhận kiểu, loại rõ ràng hoặc bản sao đối tượng:

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 }

Lưu ý: Mẫu sử dụng các hàm xác nhận TS 3.7 . Loại trả về assignvoid, không giống như Object.assign.


1

Nếu bạn đang sử dụng Bản mô tả, có lẽ bạn muốn sử dụng loại an toàn; trong trường hợp đó, đối tượng trần trụi và 'bất kỳ' bị phản đối.

Tốt hơn là không sử dụng Object hoặc {}, nhưng một số loại được đặt tên; hoặc bạn có thể đang sử dụng API với các loại cụ thể mà bạn cần mở rộng với các trường của riêng bạn. Tôi đã tìm thấy điều này để làm việc:

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

tự động gán các thuộc tính cho một đối tượng trong TypeScript.

Để làm điều đó Bạn chỉ cần sử dụng các giao diện bản thảo như vậy:

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

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

bạn có thể sử dụng nó như thế

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

1
Tôi thử với mã của bạn, nhưng tôi không nhận được bất kỳ lỗi nào: pastebin.com/NBvJifzN
pablorsk

1
cố gắng khởi tạo attributestrường bên trong SomeClass và điều đó sẽ khắc phục nó public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid

1

Thử cái này:

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

Sau đó sử dụng nó

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

0

Giải pháp duy nhất hoàn toàn an toàn cho loại này là giải pháp này , nhưng hơi dài dòng và buộc bạn phải tạo nhiều đối tượng.

Nếu bạn phải tạo một đối tượng trống trước, sau đó chọn một trong hai giải pháp này. Hãy nhớ rằng mỗi khi bạn sử dụng as, bạn sẽ mất an toàn.

Giải pháp an toàn hơn

Các loại objectan toàn bên trong getObject, mà có nghĩa là object.asẽ được loạistring | undefined

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

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

Giải pháp ngắn

Các loại objectkhông an toàn bên trong getObject, mà có nghĩa là object.asẽ loại stringngay cả trước khi phân công của nó.

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

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.