angular - Khai báo biến giao diện tùy chỉnh trong Typescript
Mình là người mới học TypeScript, và hiện tại mình đang học ngôn ngữ này trên TutorialsPoint. Hiện tại mình đang gặp khó khăn trong việc hiểu sự khác nhau giữa hai đoạn mã này.
interface Person{
age: number;
}
interface Musician extends Person{
instrument: string;
}
var drummer:Musician = {
instrument: "drum",
age: 28
}
và
interface Person{
age: number;
}
interface Musician extends Person{
instrument: string;
}
var drummer = <Musician>{}
drummer.instrument = "drum"
drummer.age = 28
Sự khác biệt giữa hai cái là gì? Và có tình huống cụ thể nào mà nên sử dụng cách triển khai thứ nhất/thứ hai không?
Cảm ơn bạn.
Câu trả lời #1
Về cuối cùng, chúng đều có cùng một ý nghĩa, nhưng cái trước được ưu tiên khi có thể.
Trong trường hợp này:
var drummer: Musician = {
instrument: "drum",
age: 28
}
bạn đang khai báo rằng drummer là một Musician bằng cách sử dụng chú thích kiểu, và gán cho nó một đối tượng literal. Trình biên dịch hài lòng với điều này vì nó có thể xác minh rằng đúng, đối tượng literal mà bạn đang gán là tương thích với giao diện Musician. Nó có một thuộc tính instrument giá trị chuỗi và một thuộc tính age giá trị số.
Bây giờ nếu chúng ta thử điều này:
var drummer: Musician = {};
// ~~~~~~~ <-- error!
// Type '{}' is not assignable to type 'Musician'.
// Property 'instrument' is missing in type '{}'.
drummer.instrument = "drum"
drummer.age = 28
Việc gán một literal đối tượng rỗng cho một giá trị được khai báo là Musician sẽ gây ra lỗi trình biên dịch. Rốt cuộc, một literal đối tượng rỗng không có thuộc tính instrument kiểu chuỗi hoặc thuộc tính age kiểu số. Và bạn đang được cảnh báo về điều đó. Giờ đây, bạn biết rằng hai dòng tiếp theo sẽ khắc phục vấn đề đó, nhưng trình biên dịch thì không.
Vì vậy bạn có thể thay đổi nó để sử dụng khai báo loại thay vì chú thích loại:
var drummer = <Musician>{}; // okay
drummer.instrument = "drum"
drummer.age = 28
Khẳng định là nơi bạn nói với trình biên dịch "đối tượng này thực sự là một Musician, mặc dù bây giờ nó không trông như vậy." Bạn đang đảm nhận trách nhiệm đảm bảo rằng drummer là một Musician và giải phóng trình biên dịch khỏi trách nhiệm xác minh điều đó cho bạn.
Và vì hai dòng tiếp theo thêm các thuộc tính cần thiết, mọi thứ đều ổn.
Cái trước được ưa thích hơn vì bạn thường muốn trình biên dịch xác minh các kiểu của bạn nếu có thể. Một khẳng định kiểu đánh đổi một phần độ an toàn, điều này ổn cho đến khi không ổn nữa, giống như việc lái xe ô tô mà không thắt dây an toàn:
var drummer = <Musician>{}; // okay
drummer.age = 28;
// whoops, forgot the instrument, but TypeScript isn't complaining
// ... later ...
console.log(drummer.instrument.toUpperCase());
// no error at compile time
// but blows up at runtime
Có những lúc bạn phải sử dụng khẳng định kiểu. Ví dụ, khi bạn có một loại tham chiếu vòng tròn nơi bạn cần xây dựng đối tượng từng phần:
interface MarriedPerson extends Person {
spouse: MarriedPerson
}
var adam: MarriedPerson = {
age: 0,
// spouse: eve <-- can't do this before eve is defined
} as MarriedPerson;
var eve: MarriedPerson = {
age: 0,
spouse: adam
}
adam.spouse = eve; // okay now
Trong phần trên, mỗi MarriedPerson cần một tham chiếu đến một MarriedPerson... nhưng sẽ không có một cái nào cho đến khi bạn tạo ra một cái. Vì vậy, bạn buộc phải sử dụng một khoảng thời gian ngắn khi một trong các đối tượng MarriedPerson không có spouse cần thiết. Và do đó, các xác nhận là cần thiết.
Điều đó có hợp lý không? Hy vọng nó có ích; chúc may mắn!
Câu trả lời #2
Các ví dụ có sự khác biệt tinh tế:
- Trong ví dụ đầu tiên, bạn đang định nghĩa một biến với kiểu
Musicianvà sau đó gán một đối tượng cho biến đó sao cho phù hợp với kiểu đó. Trong trường hợp thứ hai, bạn đang định nghĩa một biến mà không có kiểu rõ ràng (điều này có nghĩa là TypeScript sẽ suy luận kiểu cho nó), tạo một đối tượng rỗng và sau đó ép kiểu đối tượng đó thành kiểu
Musicican.- Ngoài ra, cú pháp được ưu tiên để ép kiểu trong TypeScript là
{} as Musician- cú pháp dấu ngoặc nhọn mà bạn đang sử dụng không tương thích với phần mở rộng cú pháp JSX thường dùng trong các ứng dụng React, nên nó đã được thay bằng một cái gì đó ít gây hiểu lầm hơn.
- Ngoài ra, cú pháp được ưu tiên để ép kiểu trong TypeScript là
Tôi sẽ đề xuất ví dụ đầu tiên hơn ví dụ thứ hai trong hầu hết các trường hợp - một đối tượng rỗng thực sự không triển khai Musician, vì vậy bạn đang thực sự bỏ qua trình kiểm tra kiểu ở đó!
Một tình huống duy nhất mà bạn có thể lập luận để sử dụng phương pháp thứ hai là nếu bạn có một Musician bắt đầu trống và được điền dữ liệu sau - trong trường hợp đó, tôi nghĩ bạn sẽ tốt hơn khi mô hình hóa điều đó thông qua định nghĩa kiểu bằng cách làm cho các trường là tùy chọn:
interface Person {
// '?' makes a field optional
age?: number;
}
interface Musician extends Person {
instrument?: string;
}
// The cast can now be replaced with a proper type
var drummer: Musician = {};
drummer.instrument = "drum";
drummer.age = 28;
Điều này làm rõ hơn (cả với bạn, các nhà phát triển khác và trình biên dịch) rằng có những trường hợp mà các trường đó sẽ không xác định.
