Как насчет определения Foo<T>
как сопоставленного типа, например это:
interface FooValue<T> {
default: T;
fn: (val: T) => any;
}
type Foo<T> = {
[K in keyof T]: FooValue<T[K]>
}
В этом случае, если T
- это какой-то нормальный тип объекта, например {a: string, b: number, c: boolean}
, то Foo<T>
- это Foo
-ized его версия: {a: FooValue<string>, b: FooValue<number>, c: FooValue<boolean>
}. Теперь вы можете создать вспомогательную функцию, которая принимает литерал объекта, только если он может быть выведен как Foo<T>
для некоторого типа T
:
function asFoo<T>(foo: Foo<T>): Foo<T> {
return foo;
}
Эта функция работает, потому что компилятор TypeScript может делать вывод из сопоставленные типы, позволяющие выводить T
из Foo<T>
. Вот это работает:
let foo = asFoo({
key1: { default: 'foo', fn: (val: string) => val },
key2: { default: 42, fn: (val: number) => val }
});
// inferred as { key1: FooValue<string>; key2: FooValue<number>;}
А вот и не получается:
let badFoo = asFoo(
key1: { default: 'foo', fn: (val: string) => val },
key2: { default: 42, fn: (val: number) => val },
key3: { default: true, fn: (val: string) => val }
});
// error! Types of property 'key3' are incompatible.
// Type 'boolean' is not assignable to type 'string'.
Надеюсь, это поможет. Удачи!
Обновление: в приведенном выше коде предполагается, что вы согласны с выводом foo.key1.fn('abc')
как типа any
, поскольку FooValue<string>['fn']
определяется как функция, возвращающая any
. Это как бы забывает тип вывода из исходного литерала объекта. Если вы хотите, чтобы foo
запомнил тип возвращаемого значения fn
методов его свойств, вы можете сделать эту немного другую вспомогательную функцию:
function asFoo<T, F>(foo: F & Foo<T>): F {
return foo;
}
let foo = asFoo({
key1: { default: 'foo', fn: (val: string) => val },
key2: { default: 42, fn: (val: number) => val },
// next line would cause error
// key3: { default: true, fn: (val: string)=>val}
})
const key1fnOut = foo.key1.fn('s') // known to be string
const key2fnOut = foo.key2.fn(123) // known to be number
И это работает. В этом случае asFoo()
просто проверяет, является ли вход Foo<T>
для некоторого T
, но не приводит тип вывода к Foo<T>
. В зависимости от ваших вариантов использования вы можете предпочесть это решение другому. И снова удачи.
person
jcalz
schedule
29.03.2018
interface Foo<T>
? - person Explosion Pills   schedule 29.03.2018T
при выполненииlet foo: Foo<???> = {...}
. Затем я бы установил общий тип для каждого ключа. Таким образом, всеFooValue
объекты должны быть одного типа. Но, как вы можете видеть в примере объекта: мне нужен другой общий тип для каждой пары "ключ-значение". - person Benjamin M   schedule 29.03.2018