Сохранять предполагаемый тип при ограничении типа переменной - PullRequest
0 голосов
/ 27 апреля 2018
Переменная

foo должна содержать { a: { b: string } } предполагаемый тип, в то время как ограничено соответствие другому типу, Foo. Желательно иметь тип { a: { b: string } } & Foo:

type Foo = { [k: string]: { [k: string]: string } };

const _foo = { a: { b: 'c' } };
export const foo: typeof _foo & Foo = _foo;

Но для него допустимо иметь тип { a: { b: string } } - при условии, что он выдает ошибку типа в случае, если он не соответствует Foo:

type Foo = { [k: string]: { [k: string]: string } };

function checkFoo(foo: Foo) {};

let foo = { a: { b: 'c' } };
checkFoo(foo);

Цель состоит в том, чтобы TypeScript выдавал только одну строку JavaScript:

var foo = { a: { b: 'c' } }; 

checkFoo функция также может присутствовать в выходных данных компилятора, если она не вызывается, поэтому она может быть удалена минификатором как мертвый код.

Я бы предпочел избегать ненужных выходных данных компилятора, таких как checkFoo(foo), если это возможно.

Какие здесь есть варианты? Есть ли такие, которые характерны для последних версий TypeScript, 2.7 и 2.8?

Ответы [ 3 ]

0 голосов
/ 14 мая 2018

Хотя я не обязательно согласен с предпосылкой избегания дополнительного вызова функции любой ценой, мы можем сгенерировать ошибку компилятора, которая не выдает код 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"'.

Одна из проблем этого подхода заключается в том, что нам необходимо ввести дополнительные псевдонимы / имена констант, которые загрязняют пространство имен.

Детская площадка ссылка .

0 голосов
/ 14 мая 2018

Можно было избежать ненужного вывода с помощью подхода, аналогичного описанному в в этом ответе :

type Foo = { [k: string]: { [k: string]: string } };
type CheckFoo<T extends Foo> = T;

let foo = { a: { b: 'c' } };
type _checkFoo = CheckFoo<typeof foo>; // produces an error if foo isn't compatible
0 голосов
/ 27 апреля 2018

Вы можете написать функцию:

function makeFoo<T extends Foo>(x: T): T & Foo {
    return x as T & Foo;
}
// OK, foo: what you want
export const foo = makeFoo({ a: { b: 'c' } });
// Error
export const bar = makeFoo({ a: { b: 12 } });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...