typescript - Các interface với chữ ký construct hoạt động như thế nào?
Tôi đang gặp một số khó khăn trong việc tìm hiểu cách định nghĩa các hàm tạo trong các giao diện hoạt động như thế nào. Có thể tôi hoàn toàn hiểu nhầm điều gì đó. Nhưng tôi đã tìm kiếm câu trả lời trong một thời gian khá lâu và không thể tìm thấy bất cứ điều gì liên quan đến điều này.
Làm thế nào để tôi triển khai giao diện sau trong một lớp TypeScript:
interface MyInterface {
new ( ... ) : MyInterface;
}
Anders Hejlsberg tạo một giao diện chứa thứ gì đó tương tự như thế này trong video này (khoảng phút thứ 14). Nhưng suốt đời tôi cũng không thể triển khai điều này trong một lớp.
Có lẽ tôi đang hiểu sai điều gì đó, tôi đang thiếu cái gì?
BIÊN TẬP :
Để làm rõ. Với "mới ( ... )" tôi có ý là "bất cứ thứ gì". Vấn đề của tôi là tôi không thể làm cho ngay cả phiên bản cơ bản nhất của cái này hoạt động:
interface MyInterface {
new () : MyInterface;
}
class test implements MyInterface {
constructor () { }
}
Điều này không biên dịch được cho tôi, tôi nhận được "Lớp 'test' khai báo giao diện 'MyInterface' nhưng không triển khai nó: Kiểu 'MyInterface' yêu cầu chữ ký của hàm tạo, nhưng kiểu 'test' không có một cái" khi cố gắng biên dịch nó.
BIÊN TẬP :
Vì vậy, sau khi nghiên cứu thêm về điều này dựa trên phản hồi.
interface MyInterface {
new () : MyInterface;
}
class test implements MyInterface {
constructor () => test { return this; }
}
Không phải TypeScript hợp lệ và điều này không giải quyết được vấn đề. Bạn không thể định nghĩa kiểu trả về của constructor. Nó sẽ trả về "test". Chữ ký của đoạn sau: class test { constructor() { } } Dường như là "new() => test" (được lấy bằng cách di chuột qua "class" trong trình soạn thảo trực tuyến chỉ với đoạn mã đó). Và đây là những gì chúng ta muốn và những gì tôi nghĩ rằng nó sẽ như vậy.
Có ai có thể cung cấp một ví dụ về điều này hoặc một thứ gì đó tương tự mà thực sự đang biên dịch không?
CHỈNH SỬA (một lần nữa...):
Vì vậy, tôi có thể đã nảy ra một ý tưởng về lý do tại sao có thể định nghĩa điều này trong một interface nhưng lại không thể triển khai trong một lớp TypeScript. Điều sau đây hoạt động:
var MyClass = (function () {
function MyClass() { }
return MyClass;
})();
interface MyInterface {
new () : MyInterface;
}
var testFunction = (foo: MyInterface) : void => { }
var bar = new MyClass();
testFunction(bar);
Vậy đây chỉ là một tính năng của TypeScript cho phép bạn kết nối với JavaScript thôi phải không? Hay có thể triển khai nó trong TypeScript mà không cần phải triển khai lớp bằng JavaScript?
Câu trả lời #1
Chữ ký của constructor trong các interface không thể được triển khai trong các lớp; chúng chỉ dùng để định nghĩa các API JS hiện có mà định nghĩa một hàm có thể sử dụng từ khóa 'new'. Dưới đây là một ví dụ liên quan đến chữ ký new trong interface mà hoạt động:
interface ComesFromString {
name: string;
}
interface StringConstructable {
new(n: string): ComesFromString;
}
class MadeFromString implements ComesFromString {
constructor (public name: string) {
console.log('ctor invoked');
}
}
function makeObj(n: StringConstructable) {
return new n('hello!');
}
console.log(makeObj(MadeFromString).name);
Điều này tạo ra một ràng buộc thực sự cho những gì bạn có thể gọi makeObj với:
class Other implements ComesFromString {
constructor (public name: string, count: number) {
}
}
makeObj(Other); // Error! Other's constructor doesn't match StringConstructable
Câu trả lời #2
Trong quá trình tìm kiếm câu hỏi giống hệt, tôi đã đi tìm cách mà Nhóm TypeScript đã làm...
Họ đang khai báo một interface và sau đó là một biến với tên trùng khớp chính xác với tên interface. Đây cũng là cách để gõ kiểu cho các hàm tĩnh.
Ví dụ từ lib.d.ts:
interface Object {
toString(): string;
toLocaleString(): string;
// ... rest ...
}
declare var Object: {
new (value?: any): Object;
(): any;
(value: any): any;
// ... rest ...
}
Tôi đã thử điều đó và nó hoạt động rất hiệu quả.
Câu trả lời #3
Chà, một interface với chữ ký construct không nhằm để bất kỳ lớp nào triển khai (lúc đầu nhìn qua điều này có thể hơi lạ đối với những người có nền tảng C#/Java như tôi nhưng hãy thử cho nó một cơ hội). Nó hơi khác một chút.
Trong khoảnh khắc, hãy nghĩ nó như một giao diện với chữ ký hàm (giống như @FunctionalInterface trong thế giới Java). Mục đích của nó là để mô tả một loại hàm... phần nào. Chữ ký được mô tả này được cho là phải được thỏa mãn bởi một đối tượng hàm... nhưng không phải bất kỳ hàm cấp cao hoặc phương thức nào. Nó nên là một hàm biết cách tạo ra một đối tượng, một hàm được gọi khi từ khóa new được sử dụng.
Vì vậy, một interface với chữ ký construct định nghĩa chữ ký của một constructor! Constructor của lớp bạn nên tuân thủ chữ ký được định nghĩa trong interface (hãy nghĩ về nó như là constructor thực hiện interface). Nó giống như một nhà máy!
Đây là một đoạn mã ngắn cố gắng minh họa cách sử dụng phổ biến nhất:
interface ClassicInterface { // old school interface like in C#/Java
method1();
...
methodN();
}
interface Factory { //knows how to construct an object
// NOTE: pay attention to the return type
new (myNumberParam: number, myStringParam: string): ClassicInterface
}
class MyImplementation implements ClassicInterface {
// The constructor looks like the signature described in Factory
constructor(num: number, s: string) { } // obviously returns an instance of ClassicInterface
method1() {}
...
methodN() {}
}
class MyOtherImplementation implements ClassicInterface {
// The constructor looks like the signature described in Factory
constructor(n: number, s: string) { } // obviously returns an instance of ClassicInterface
method1() {}
...
methodN() {}
}
// And here is the polymorphism of construction
function instantiateClassicInterface(ctor: Factory, myNumberParam: number, myStringParam: string): ClassicInterface {
return new ctor(myNumberParam, myStringParam);
}
// And this is how we do it
let iWantTheFirstImpl = instantiateClassicInterface(MyImplementation, 3.14, "smile");
let iWantTheSecondImpl = instantiateClassicInterface(MyOtherImplementation, 42, "vafli");
Câu trả lời #4
Từ tài liệu chính thức
Điều này là vì khi một lớp triển khai một giao diện, chỉ phía thể hiện của lớp được kiểm tra. Vì hàm khởi tạo nằm ở phía tĩnh, nó không được bao gồm trong kiểm tra này.
Thay vào đó, bạn sẽ cần làm việc trực tiếp với phần tĩnh của lớp. Trong ví dụ này, chúng ta định nghĩa hai giao diện, ClockConstructor cho hàm tạo và ClockInterface cho các phương thức của thể hiện. Sau đó, để tiện lợi, chúng ta định nghĩa một hàm tạo createClock tạo các thể hiện của kiểu được truyền vào nó:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
