我有下面的TS代码,其中包含多个接口、组件和一个键/接口Map。
interface FooProps {
'keyInFoo': string
}
const Foo = (props: FooProps) => {}
interface BarProps {
'keyInBar': string
}
const Bar = (props: BarProps) => {}
interface PropsMap {
'foo': FooProps,
'bar': BarProps,
//.. baz, etc...
}
type keyName = keyof PropsMap
const MyFunc = <T extends keyName>(key: T) => {
const props: PropsMap[T] = getProps(key)
switch(key) {
// We should know since T == typeof key === 'foo'
// that typeof props === FooProps
case 'foo': return Foo(props) // <- ERROR: Property ''keyInFoo'' is missing in type 'BarProps'
case 'bar': return Bar(props) // <- ERROR Property ''keyInBar'' is missing in type 'FooProps'
}
}
const getProps = <T extends keyName>(key: T): PropsMap[T] => {
// ... some logic to get the props
return {} as PropsMap[T]
}
我想对map的键运行switch/if-else语句,并让TypeScript知道每种情况下props
的值。
当然,我可以在每种情况下Assertprops as FooProps
等,但是我觉得应该有一种方法让TS推断类型。
我也尝试过使用泛型类型作为map,但没有取得额外的成功:
type PropsGeneric<T extends keyName> =
T extends 'foo' ? FooProps :
T extends 'bar' ? BarProps : never;
见TSPlayground
我提出的一个可行但冗长的解决方案是为每个键创建一个类型保护,并在MyFunc
中的一系列if-else if语句中使用它
const isFoo = <T extends keyName>(key: T, props: PropsMap[T]): props is FooProps => key === 'foo'
const isBar = <T extends keyName>(key: T, props: PropsMap[T]): props is BarProps => key === 'bar'
// ... etc
1条答案
按热度按时间umuewwlo1#
正如您已经注意到的,当涉及泛型时,要使TypeScript理解函数实现的类型是很棘手的。当两个都是泛型类型时,通过检查
key
的值来缩小props
的类型是不起作用的。两个类型都不受编译器的影响。我首先要修改的是
PropsMap
,让我们使用函数的类型作为值类型,这样我们就可以在以后使用ReturnType
和Parameters
实用程序类型。有几种方法可以编写条件语句,以便TypeScript正确地理解它们。它们大多类似于某种类型的查找对象,可以是强类型的。
我们名为
ret
的查找对象是用Map类型定义的。对于PropsMap
中的每个键,ret
也有一个对应的键,该键保存一个方法。每个方法都以props
作为其参数。props
参数和方法的返回类型严格地由Map类型定义。剩下要做的就是用
props
调用ret
。这给我们留下了一个非常冗长的实现,而且由于增加了函数调用,还带来了一些运行时开销,但这是一种无需类型Assert就可以实现强类型化的方法。
Playground