Когда использовать классы и интерфейсы в библиотеке TypeScript?

Я видел связанные вопросы, такие как Вот этот, но все они предполагают, что разница между классами и интерфейсами заключается в том, что классы могут быть созданный, а интерфейсы - нет. Однако возьмем, к примеру, @types/google.maps. Например:

declare namespace google.maps {
  class Map extends google.maps.MVCObject {
    constructor(mapDiv: HTMLElement, opts?: google.maps.MapOptions);
    controls: google.maps.MVCArray<any>[];
    data: google.maps.Data;
    fitBounds(
        bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral,
        padding?: number|google.maps.Padding): void;
    // ...
  }
}
declare namespace google.maps {
  interface MapType {
    alt: string|null;
    getTile(
        tileCoord: google.maps.Point|null, zoom: number,
        ownerDocument: Document|null): Element|null;
    maxZoom: number;
    minZoom: number;
    name: string|null;
    projection: google.maps.Projection|null;
    radius: number;
    releaseTile(tile: Element|null): void;
    tileSize: google.maps.Size|null;
  }
}
declare namespace google.maps {
  interface MapTypeControlOptions {
    mapTypeIds?: (string)[]|null;
    position?: google.maps.ControlPosition|null;
    style?: google.maps.MapTypeControlStyle|null;
  }
}
declare namespace google.maps {
  class MapTypeRegistry extends google.maps.MVCObject {
    set(id: string, mapType: any): void;
  }
}
declare namespace google.maps {
  interface MapsEventListener {
    remove(): void;
  }
}
declare namespace google.maps {
  interface WebglCameraParams extends google.maps.CameraParams {
    lat: number;
    lng: number;
  }
}

И интерфейсы, и классы:

  • Расширьте другие объекты.
  • Иметь свойства.
  • Имейте методы/функции.
  • Невозможно напрямую создать экземпляр из этих определений.

Где-то в коде Google Maps на самом деле есть класс как для class, определенных здесь, так и для interface, определенных здесь. Похоже, что определения классов предназначены для того, что может создать пользователь, а не для того, что библиотека может создать внутри (интерфейсы).

Итак, каковы более глубокие различия между интерфейсами и классами в этих контекстах? Мне нравится то, что говорит другой ответ, что классы обычно являются конструируемыми, но эти классы явно не создаются напрямую. Они представляют другие классы. Но интерфейсы представляют и другие классы. Так что я в замешательстве.

«И интерфейсы, и классы: ... Не могут быть созданы напрямую из этих определений». Какие интерфейсы и классы вы там имеете в виду? Вы можете создать экземпляр Map из этих определений (и я делал это много раз), и я ожидаю, что смогу создать экземпляр MapTypeRegistry (хотя я не могу сказать, что у меня есть). (Очевидно, что у вас должны быть объекты среды выполнения для резервного копирования определений типов.)
T.J. Crowder 17.03.2022 13:38

Я имею в виду, что это не буквальныйclass X { } в JS, это определено в машинописном тексте, как видно из незаполненных методов. Литеральный класс определен в библиотеке карт Google.

Lance 17.03.2022 13:44

Обратите внимание, что вы смотрите на файл .d.ts (о чем свидетельствует часть @types. Он никогда не будет содержать никакого реального кода, только определения типов для обычного файла JS. В реальном проекте машинописи вы обнаружите, что классы имеют полный определение, в то время как интерфейсы по-прежнему доступны только для определений.

Alejandro 17.03.2022 13:47

Я понимаю. Вот почему я спрашиваю, почему они определили их как классы, а не интерфейсы.

Lance 17.03.2022 13:48
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
2
4
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

(от ваш комментарий):

I'm asking why they defined them as classes and not interfaces.

Между type, interface и class есть совпадения (вы это прекрасно знаете, но мне нужно с чего-то начинать :-)) , но class делает три вещи, а не только одну вещь, которую сделали бы interface или type. Давайте рассмотрим более простой пример:

declare namespace example1 {
    class Example1 {
        a: string;
        constructor(a: string);
        static method(): void;
    }
}

Тот:

  • Определяет тип Example1, который описывает экземпляры.
  • Определяет, что существует привязка времени выполнения, Example1, которая ссылается на функцию-конструктор для этих экземпляров. (Это не привязка Создайте, она просто говорит, что она есть.)
  • (Неявно) Определяет тип (typeof Example1), который является типом функции-конструктора.

Если вы хотите написать то же самое без class, вам придется написать каждую из этих трех вещей самостоятельно, например, example2 здесь:

declare namespace example2 {
    // Define the type for instances
    interface Example2 {
        a: string;
    }
    // Define that there is a binding for the constructor function
    let Example2: {                 //
        new (a: string): Example2;  // Define the type for the constructor function
        method(): void;             //
    };                              //
}

Вы даже можете пойти дальше и объявить именованный тип для функции-конструктора:

declare namespace example3 {
    // Define the type for instances
    interface Example3 {
        a: string;
    }
    // Define the type for the constructor function
    interface Example3Constructor {
        new (a: string): Example3;
        method(): void;
    }
    // Define that there is a binding for the constructor function
    let Example3: Example3Constructor;
}

(Это то, что lib.es5.d.ts делает, скажем, для Array, за исключением того, что вместо var используется let.)

Как всегда, когда есть несколько способов сделать что-то, некоторые люди сделают это одним способом, другие - другим, но есть некоторый аргумент в пользу использования class, когда он описывает все части, которые вы пытаетесь описать сразу.

Другие вопросы по теме