Ограничение общих c аргументов функции до универсального c типа интерфейса - PullRequest
2 голосов
/ 28 мая 2020

В Typescript у меня есть общий c интерфейс, представляющий тип, который я хочу передать функции.

//foo must be an object {}, not a number or string or boolean or array
interface MyInterface<T extends {[key: string]: any}> {
  foo: T
}

Поэтому я делаю свою функцию generi c, но TS не выводит ограничение из моего интерфейса

const myGenericFn: <T, U extends MyInterface<T>>(bar: U) => void = (bar) => {
  //Why is T not constrained to {[key: string]: any}? 
  //Shouldn't the constraint be inferred from MyInterface<T>?
  // Or be an error since T doesn't extend {[key: string]: any}?


  //bar.foo is T but T does not extend {[key: string]: any}! 
  const baz = bar.foo

}

//No error! Should be constrained to {foo: {[key: string]: any}} but accepts {foo: string}!
myGenericFn({foo: "bar"})

Единственный способ, который я вижу для выполнения этой работы, - это повторить ограничение на T вот так

const myGenericFnNotDRY: <T extends {[key: string]: any}, U extends MyInterface<T>>(bar: U) => void = (bar) => {
  //Not DRY, I have to repeat my generic constraint everywhere I want to use MyInterface within a generic fn!

  //bar.foo now correctly extends {[key: string]: any}
  const baz = bar.foo
}

//Errors as expected, string not assignable to {[key: string]: any}
myGenericFnNotDRY({foo: "bar"})

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

TS Playground для вышеуказанного кода

Почему TS не выводит ограничение на T из MyInterface? Как мне ввести мою функцию, чтобы T был правильно ограничен ограничением, указанным c на MyInterface?

1 Ответ

0 голосов
/ 28 мая 2020

Я бы посоветовал сделать одно из двух:

// Solution 1: use conditional types to make sure you can never pass a "foo"
// that is not a Record<string, unknown>
interface MyInterface1<T> {
  foo: T extends Record<string, unknown> ? T : never;
}

const myGenericFn1: <T>(bar: MyInterface1<T>) => void = (bar) => {
  // ...
}

// Yaaaay! Error! I mean shit
myGenericFn1({ foo: "bar" });

// No error here though, just to check
myGenericFn1({ foo: { something: "somewhere" } });

// Solution 2: Use type parameter constraint using unknown rather than any
// 
// This solution though has the same drawback you didn't like in your original
// post - you need to repeat the constraint in front of your function
interface MyInterface2<T extends Record<string, unknown>> {
  foo: T;
}

const myGenericFn2: <T extends Record<string, unknown>>(bar: MyInterface2<T>) => void = (bar) => {
  // ...
}

// Yaaaay! Error! I mean shit
myGenericFn2({ foo: "bar" });

// No error here though, just to check
myGenericFn1({ foo: { something: "somewhere" } });

Игровая площадка TypeScript здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...