Định nghĩa kiểu trong đối tượng bằng chữ trong TypeScript


343

Trong các lớp TypeScript, có thể khai báo các kiểu cho các thuộc tính, ví dụ:

class className {
  property: string;
};

Làm thế nào để khai báo loại tài sản trong một đối tượng theo nghĩa đen?

Tôi đã thử mã sau đây nhưng nó không biên dịch:

var obj = {
  property: string;
};

Tôi nhận được lỗi sau:

Tên 'chuỗi' không tồn tại trong phạm vi hiện tại

Tôi đang làm gì đó sai hay đây là một lỗi?

Câu trả lời:


423

Bạn khá gần, bạn chỉ cần thay thế =bằng a :. Bạn có thể sử dụng một loại đối tượng bằng chữ (xem phần spec 3.5.3) hoặc một giao diện. Sử dụng một loại đối tượng theo nghĩa đen gần với những gì bạn có:

var obj: { property: string; } = { property: "foo" };

Nhưng bạn cũng có thể sử dụng một giao diện

interface MyObjLayout {
    property: string;
}

var obj: MyObjLayout = { property: "foo" };

14
Tùy chọn DRY (Đừng lặp lại chính mình) của @Rick Love với nhà điều hành diễn viên có vẻ gọn gàng hơn nhiều. Chỉ cần cho ý kiến, không downvoting ...
spechter

Tôi biết điều này đã được trả lời cách đây một thời gian, nhưng tôi có nhầm lẫn trong các giả định rằng các vấn đề có thể phát sinh khi lớp có các phương thức cũng như các thuộc tính không? Vì lớp không được khởi tạo và chúng tôi chỉ gán các thuộc tính, nên gọi một phương thức trên lớp sẽ gây ra ngoại lệ null. Về cơ bản, đối tượng chúng ta chỉ tạo 'hành động' là lớp vì chúng ta gán loại của nó, nhưng thực tế nó không phải là một thể hiện của lớp đó. Tức là chúng ta cần tạo lớp với từ khóa 'mới'. Và sau đó chúng tôi quay lại quảng trường 1, vì chúng tôi không thể làm một cái gì đó như lớp mới () {prop: 1}; trong TS như chúng ta có thể trong C # chẳng hạn.
DeuxAlpha

@DeuxAlpha Đây là gán cho một giao diện , không thể có phương thức. Tôi không tin rằng bạn có thể chỉ định cho một lớp học như thế này.
Mog0

liên kết đến thông số sẽ tốt đẹp :)
ahong

@DeuxAlpha đây là tạo một đối tượng theo nghĩa đen, không phải là một đối tượng lớp. Ngoài ra một giao diện có thể có phương pháp. Nếu giao diện của bạn định nghĩa một phương thức, thì đối tượng bằng chữ cũng phải định nghĩa nó - nó sẽ không có giá trị. Đối tượng bằng chữ phải hoàn thành mọi thứ mà giao diện xác định hoặc hệ thống loại sẽ hiển thị lỗi.
Rick Tình yêu

290

Cập nhật 2019-05-15 (Mẫu mã được cải tiến thay thế)

Sau nhiều năm sử dụng constvà hưởng lợi từ nhiều mã chức năng hơn, tôi sẽ khuyên bạn không nên sử dụng phần dưới đây trong hầu hết các trường hợp. (Khi xây dựng các đối tượng, việc buộc hệ thống loại thành một loại cụ thể thay vì để nó suy ra các loại thường là một dấu hiệu cho thấy có gì đó không đúng).

Thay vào đó, tôi khuyên bạn nên sử dụng constcác biến càng nhiều càng tốt và sau đó soạn đối tượng làm bước cuối cùng:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • Điều này sẽ gõ đúng mọi thứ mà không cần gõ rõ ràng.
  • Không cần phải gõ lại tên trường.
  • Điều này dẫn đến mã sạch nhất từ ​​kinh nghiệm của tôi.
  • Điều này cho phép trình biên dịch cung cấp xác minh trạng thái nhiều hơn (ví dụ: nếu bạn quay lại ở nhiều vị trí, trình biên dịch sẽ đảm bảo cùng một loại đối tượng luôn được trả về - điều này khuyến khích bạn khai báo toàn bộ giá trị trả về tại mỗi vị trí - cho thấy rõ ràng hoàn toàn ý định của giá trị đó).

Bổ sung 2020-02-26

Nếu bạn thực sự cần một loại mà bạn có thể được khởi tạo một cách lười biếng: Đánh dấu nó là một loại kết hợp nullable (null hoặc Type). Hệ thống loại sẽ ngăn bạn sử dụng nó mà không đảm bảo trước tiên nó có giá trị.

Trong tsconfig.json, đảm bảo bạn kích hoạt kiểm tra null nghiêm ngặt:

"strictNullChecks": true

Sau đó sử dụng mẫu này và cho phép hệ thống loại bảo vệ bạn khỏi quyền truy cập vô tình / không xác định:



const state = {
    instance: null as null | ApiService,
    // OR
    // instance: undefined as undefined | ApiService,

};

const useApi = () => {
    // If I try to use it here, the type system requires a safe way to access it

    // Simple lazy-initialization 
    const api = state?.instance ?? (state.instance = new ApiService());
    api.fun();

    // Also here are some ways to only access it if it has value:

    // The 'right' way: Typescript 3.7 required
    state.instance?.fun();

    // Or the old way: If you are stuck before Typescript 3.7
    state.instance && state.instance.fun();

    // Or the long winded way because the above just feels weird
    if (state.instance) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans way
    if (state.instance != null) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans 
    // AND I was told to always use triple === in javascript even with null checks way
    if (state.instance !== null && state.instance !== undefined) { state.instance.fun(); }
};

class ApiService {
    fun() {
        // Do something useful here
    }
}

Không làm như sau trong 99% trường hợp:

Cập nhật 2016 / 02-10 - Để xử lý TSX (Cảm ơn @Josh)

Sử dụng astoán tử cho TSX.

var obj = {
    property: null as string
};

Một ví dụ dài hơn:

var call = {
    hasStarted: null as boolean,
    hasFinished: null as boolean,
    id: null as number,
};

Câu trả lời gốc

Sử dụng toán tử cast để tạo ra súc tích này (bằng cách chuyển null thành loại mong muốn).

var obj = {
    property: <string> null
};

Một ví dụ dài hơn:

var call = {
    hasStarted: <boolean> null,
    hasFinished: <boolean> null,
    id: <number> null,
};

Điều này tốt hơn nhiều so với việc có hai phần (một phần để khai báo các loại, phần thứ hai để khai báo mặc định):

var callVerbose: {
    hasStarted: boolean;
    hasFinished: boolean;
    id: number;
} = {
    hasStarted: null,
    hasFinished: null,
    id: null,
};

10
Nếu bạn đang sử dụng TSX (TS với JSX), bạn không thể sử dụng cách đặt tên khung góc, vì vậy những dòng đó trở thành một cái gì đó giống như property: null as stringtrong đó sự khác biệt quan trọng là astoán tử.
Josh

2
@RickLove Điều này thực sự hạn chế loại biến đối tượng, hay đây chỉ là một cách để chỉ định các loại khi được gán? Nói cách khác, sau khi bạn gán cho biến calltrong ví dụ thứ hai, bạn có thể gán một loại hoàn toàn khác cho nó không?
Daniel Griscom

3
Điều này nên được trả lời
Drew R

4
không làm việc. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
slideshowp2

2
Đây chỉ là một sự lạm dụng một tính năng của ngôn ngữ hay đây thực sự là hợp pháp? Bạn có thể cung cấp liên kết để đọc mor đến các tài liệu chính thức? Cảm ơn!
Qwerty

31

Tôi ngạc nhiên khi không ai nhắc đến điều này nhưng bạn chỉ có thể tạo một giao diện được gọi là ObjectLiteralchấp nhậnkey: value các cặp loại string: any:

interface ObjectLiteral {
  [key: string]: any;
}

Sau đó, bạn sẽ sử dụng nó, như thế này:

let data: ObjectLiteral = {
  hello: "world",
  goodbye: 1,
  // ...
};

Một phần thưởng bổ sung là bạn có thể sử dụng lại giao diện này nhiều lần nếu bạn cần, trên bao nhiêu đối tượng bạn muốn.

Chúc may mắn.


3
Đây là một ý tưởng tồi, nó làm cho hệ thống loại không có giá trị. Điểm quan trọng của bản thảo là cho phép hệ thống loại giúp ngăn ngừa lỗi và cũng giúp cung cấp chức năng tự động hoàn thành tốt hơn trong công cụ - điều này về cơ bản vô hiệu hóa tất cả các lợi ích của Bản mô tả. Sẽ tốt hơn nếu không sử dụng bất kỳ giao diện nào trong ví dụ trên.
Rick Yêu

1
@RickLove Tôi rất không đồng ý. Điều này đặc biệt hữu ích khi một số thuộc tính là tùy chọn nhưng chúng tôi vẫn muốn xác định rõ ràng tất cả các loại trong (ví dụ: chứa các đối số cho hàm). Điều này có thể được xem đơn giản là đường cú pháp và thực sự hoạt động giống như một toán tử trải rộng cho các thuộc tính của Giao diện .
CPHPython

@CPHPython tại sao không chỉ định các tham số tùy chọn với các loại cụ thể của chúng? Lần duy nhất điều này có ý nghĩa là nếu tên không được biết tại thời điểm mã (Tức là chúng đến từ cơ sở dữ liệu hoặc nguồn bên ngoài). Ngoài ra đối với sự kết hợp phức tạp của các đối số, các loại kết hợp làm việc tuyệt vời. Nếu bạn đang mã hóa theo tên cụ thể, chúng nên được xác định nếu có thể. Nếu nó chỉ là dữ liệu không ảnh hưởng đến logic, thì dĩ nhiên loại bỏ nó khỏi hệ thống loại.
Rick Love

1
@RickLove "tên không được biết tại thời điểm mã" -> đây là một ví dụ hay và thực tế, các giá trị của các loại khóa đó đã được biết và chúng đều giống nhau (ví dụ: chuỗi ). Lưu ý rằng tôi chỉ ủng hộ việc sử dụng [key: string]một phần, không phải bất kỳ phần nào như định nghĩa loại giá trị ... Điều đó thực sự sẽ loại bỏ tính hữu dụng của các loại.
CPHPython

1
Làm thế nào tôi có thể đi nếu tôi muốn cho phép chuỗi và số được thêm nhưng không phải loại khác?
DonkeyBanana

14

Nếu bạn đang cố gắng viết một chú thích kiểu, cú pháp là:

var x: { property: string; } = ...;

Nếu bạn đang cố gắng viết một đối tượng bằng chữ, cú pháp là:

var x = { property: 'hello' };

Mã của bạn đang cố gắng sử dụng tên loại ở vị trí giá trị.


10
Của bạn var x: { property: string; } = ...;là một trêu chọc! Tôi hy vọng dấu chấm lửng là cú pháp hợp lệ để viết tắt var x: { property: string; } = { property: 'hello' };.
Jason Kleban

10

Trong TypeScript nếu chúng ta khai báo đối tượng thì chúng ta sẽ sử dụng cú pháp sau:

[access modifier] variable name : { /* structure of object */ }

Ví dụ:

private Object:{ Key1: string, Key2: number }

7

Nếu bạn đang cố gắng thêm các kiểu chữ vào một đối tượng bị phá hủy theo nghĩa đen, ví dụ như trong các đối số cho một hàm, cú pháp là:

function foo({ bar, baz }: { bar: boolean, baz: string }) {
  // ...
}

foo({ bar: true, baz: 'lorem ipsum' });

5

Nếu thuộc tính của bạn có cùng loại, bạn có thể sử dụng loại tiện ích được xác định trước Record:

type Keys = "property" | "property2"

var obj: Record<Keys, string> = {
  property: "my first prop",
  property2: "my second prop",
};

Tất nhiên bạn có thể đi xa hơn và xác định loại tùy chỉnh cho các giá trị thuộc tính của bạn:

type Keys = "property" | "property2"
type Value = "my prop" | "my other allowed prop"

var obj: Record<Keys, Value> = {
  property: "my prop",
  property2: "my second prop", // TS Error: Type '"my second prop"' is not assignable to type 'Value'.
};

2
Chính xác những gì tôi cần. Cảm ơn! Bản đánh máy cho chiến thắng!
Audacitus

Tôi cũng sẽ sử dụng letthay vì varở đây.
Fwd079

3
// Use ..

const Per = {
  name: 'HAMZA',
  age: 20,
  coords: {
    tele: '09',
    lan: '190'
  },
  setAge(age: Number): void {
    this.age = age;
  },
  getAge(): Number {
    return age;
  }
};
const { age, name }: { age: Number; name: String } = Per;
const {
  coords: { tele, lan }
}: { coords: { tele: String; lan: String } } = Per;

console.log(Per.getAge());

3
Chào mừng bạn đến với SO! Bạn có thể mở rộng câu trả lời của bạn không, nó sẽ hữu ích để giải thích làm thế nào / tại sao điều này hoạt động.
tiệc tùng

1

Trong mã của bạn:

var obj = {
  myProp: string;
};

Bạn đang thực sự tạo một đối tượng bằng chữ và gán chuỗi biến cho thuộc tính myProp. Mặc dù thực tế rất tệ nhưng đây thực sự sẽ là mã TS hợp lệ (không sử dụng mã này!):

var string = 'A string';

var obj = {
  property: string
};

Tuy nhiên, những gì bạn muốn là các đối tượng theo nghĩa đen được gõ. Điều này có thể đạt được theo nhiều cách khác nhau:

Giao diện:

interface myObj {
    property: string;
}

var obj: myObj = { property: "My string" };

Loại tùy chỉnh:

type myObjType = {
    property: string
};

var obj: myObjType = { property: "My string" };

Toán tử diễn viên:

var obj = {
    myString: <string> 'hi',
    myNumber: <number> 132,
};

Loại đối tượng theo nghĩa đen:

var obj: { property: string; } = { property: "Mystring" };
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.