Как я могу сделать встроенную проверку типов без хранения в TypeScript? - PullRequest
1 голос
/ 11 июля 2019

У меня есть некоторый интерфейс

ITestInterface {
  foo: string;
}

Я бы хотел передать экземпляр этого интерфейса в качестве аргумента функции.Функция принимает любой тип объекта, поэтому она не проверяет тип самостоятельно.Чтобы удостовериться, что объект имеет правильный тип, я могу использовать хранилище:

const passMe: ITestInterface = { foo: "bar" };
someFunction(passMe);

Но я хотел бы иметь способ создания аргумента в строке, в то же время проводя проверку типа.

// made up example syntax
someFunction({ foo: "bar" } istype ITestInterface);

Есть ли хороший способ что-то наподобие приведенного выше примера inline?

Я пробовал использовать as, но он не ограничивает тип.Например, допустимо следующее:

someFunction({ foo: "bar", hello: true } as ITestInterface);

Еще одна вещь, которую я могу сделать в этом случае, это изменить someFunction для создания шаблонов, но это не то, что я считаю хорошим решением.Я не всегда буду иметь эту привилегию.

someFunction<TYPE>(arg: TYPE) {
  // modify function definition
}

someFunction<ITestInterface>({foo: "bar"});

Ответы [ 2 ]

1 голос
/ 11 июля 2019

Во-первых, интерфейсы должны быть реализованы классом. Ни интерфейсы, ни классы не должны использоваться для проверки типов простых объектов в TypeScript - просто используйте вместо них типы. Кроме того, I перед именем интерфейса обозначает интерфейс, поэтому вы должны написать ITest вместо ITestInterface:

// replace this:
ITestInterface { foo: string }
// by this:
type Test = { foo: string }

Теперь давайте отложим в сторону эти замечания уровня разработки программного обеспечения и рассмотрим суть проблемы:

Если вы хотите убедиться, что someFunction всегда вызывает объекты типа Type, достаточно написать определение функции, как показано ниже, потому что TypeScript будет обнаруживать в любом месте кода, где он вызывается с чем-то другим:

// like this
const someFunction: (arg: Type) => any = (arg) => { /*...*/ }
// or like this
function someFunction(arg: Type): any { /*...*/ }

Если у вас есть аргумент, что вы знаете, относится к типу Type, но каким-то образом компилятор TS этого не выводит, вот где вы используете ключевое слово as.

someFunction({foo: 10}); // error
someFunction({foo: 'bar'});
someFunction({foo: Math.random()<1 ? 'bar' : 10}); // error
someFunction({foo: Math.random()<1 ? 'bar' : 10} as Type);

И это в основном все, что вам нужно сделать, чтобы сделать вашу программу безопасной во время компиляции. Проверьте код выше на игровой площадке TypeScript .


Если по какой-либо причине вы хотите добавить дополнительный уровень надежности и убедиться, что ваша программа безопасна для типов во время выполнения, вам придется проверять тип во время выполнения. Это может привести к снижению производительности, но если вы просто хотите убедиться, что несколько свойств находятся на объекте, это может занимать только одну строку в определении функции:

const someSafeFunction(arg: Type): any {
  if (Object.keys(arg).sort().join(',')!='propertyName1,propertyName2') throw new Error('Invalid argument type');
  /* ... */
}
1 голос
/ 11 июля 2019

Специальной функции, которую вы ищете, например, «аннотации типов для произвольных выражений», в TypeScript не существует. Я только что искал существующий запрос функции в GitHub, но я не могу найти его. Либо его там нет, либо мои силы поиска недостаточно сильны.

Здесь есть несколько способов, каждый со своими проблемами.


Как вы уже видели, проще всего использовать утверждение типа . Это работает, чтобы не дать вам ввести совершенно не связанный тип:

// assertion
someFunction({ foo: "bar" } as ITestInterface); // okay as expected
someFunction({ unrelatedThing: 1 } as ITestInterface); // error as expected

Он также позволяет использовать дополнительные свойства (которые по-прежнему безопасны и безопасны для типов, объект типа ITestInterface не гарантирует не других свойств ... это может вас удивить, потому что вы ожидаете проверка избыточного свойства , но это случается только когда-нибудь):

someFunction({ foo: "bar", hello: true } as ITestInterface); // okay by design,
// excess properties are allowed

Но главное, что здесь важно, - утверждения типа позволяют небезопасно сужать типы, поэтому следующее не будет ошибкой:

someFunction({} as ITestInterface); // no error ?! assertions also NARROW types

Другой путь, по которому вы могли бы пойти, - создать вспомогательную функцию с именем isType, например:

// helper function
const isType = <T>(x: T) => x;

Это ведет себя почти так же, как вы хотели бы:

someFunction(isType<ITestInterface>({ foo: "bar" })); // okay as expected
someFunction(isType<ITestInterface>({ unrelatedThing: 1 })); // error as expected

someFunction(isType<ITestInterface>({ foo: "bar", hello: true })); // error as you want
someFunction(isType<ITestInterface>({})); // error as everyone wants

Но, как вы сказали, это может не стоить этого для вас. Большинство исполняющих движков будут счастливо встроены в функции, такие как x => x, поэтому я не думаю, что это проблема производительность . Но это может быть проблемой элегантности, которая зависит от вас.


В любом случае, это лучшее, что я могу сделать. Надеюсь, это поможет. Удачи!

Ссылка на код

...