Хотя я не обязательно согласен с предпосылкой избегания дополнительного вызова функции любой ценой, мы можем сгенерировать ошибку компилятора, которая не выдает код JS, если foo
не реализует ожидаемый интерфейс без использования функций.
Подход основан на создании условного типа (доступного в 2.8), который возвращает различные строковые литеральные типы в зависимости от того, реализует ли тип константы интерфейс или нет. Тогда мы можем использовать этот тип в месте, где мы ожидаем определенный тип литерала.
Чтобы получить ошибку, нам нужно будет использовать тип таким образом, чтобы он не генерировал код, и я нашел 2 возможных способа, либо в объявлении, либо в псевдониме типа.
export type Foo = { [k: string]: { [k: string]: string } };
type TestType<T, TBase> = T extends TBase ? "OK" : "Implementation does not extend expected type";
type Validate<T extends "OK"> = "OK";
let foo = { a: { b: 'c' } };
// use type in a constant
declare const fooTest : Validate<TestType<typeof foo, Foo>> // ok
// use type in a type alias
type fooTest = Validate<TestType<typeof foo, Foo>> //ok
let fooBad = { a: { b: 10 } };
declare const fooBadTest : Validate<TestType<typeof fooBad, Foo>>; // error: Type '"Implementation does not extend expected type"' does not satisfy the constraint '"OK"'
type fooBadTest = Validate<TestType<typeof fooBad, Foo>> // error: Type '"Implementation does not extend expected type"' does not satisfy the constraint '"OK"'.
Одна из проблем этого подхода заключается в том, что нам необходимо ввести дополнительные псевдонимы / имена констант, которые загрязняют пространство имен.
Детская площадка ссылка .