typescript:如何从受查找类型泛型返回联合约束的泛型函数返回正确的类型

typescript:如何从受查找类型泛型返回联合约束的泛型函数返回正确的类型,typescript,generics,Typescript,Generics,假设我们有以下函数实现: type Action = 'GREET' |'ASK' function getUnion<T extends Action>(action: T) { switch (action) { case 'GREET': return {hello: 'Guten Tag!'} as const case 'ASK': return {time: 'Wie spat is es?'} as const default:

假设我们有以下函数实现:

type Action = 'GREET' |'ASK'

function getUnion<T extends Action>(action: T) {
  switch (action) {
  case 'GREET':
    return {hello: 'Guten Tag!'} as const
  case 'ASK':
    return {time: 'Wie spat is es?'} as const
  default:
    return 'WUT?'
  }
}
type Action='GREET'|'ASK'
函数getUnion(操作:T){
开关(动作){
案例“问候”:
以常量返回{hello:'Guten Tag!'}
案例“询问”:
返回{time:'Wie spat is es?}作为常量
违约:
“呜?”
}
}
此函数的返回类型如下:

{“WUT?”{hello:'Guten Tag!';time?:未定义;}{time:'Wie spat is es?';hello?:未定义;}

因此,我们可能会认为,如果我们使用开关案例中使用的泛型by“discriminant union”类型的函数约束,它将返回特定的分支类型,如下所示:

// So what I came up with is following:

This implementation mitigates previous issue, as it properly returns narrowed type from return union by used function argument, via conditional types mapper:

type ProperReturn<T> = T extends 'GREET' ? {hello:'Guten Tag!'} : T extends 'ASK' ? {time:'Wie spat is es?'} : 'WUT'

/所以我想到的是:

// So what I came up with is following:

This implementation mitigates previous issue, as it properly returns narrowed type from return union by used function argument, via conditional types mapper:

type ProperReturn<T> = T extends 'GREET' ? {hello:'Guten Tag!'} : T extends 'ASK' ? {time:'Wie spat is es?'} : 'WUT'
此实现缓解了以前的问题,因为它通过条件类型映射器通过使用的函数参数正确地从返回union返回缩小的类型:

function getUnionStrict<T extends Action>(action: T): ProperReturn<T> {
  switch (action) {
  case 'GREET':
    // One option is to use a mapping interface instead of using a conditional type, makes the return type easier to follow. Also I generally use a separate implementation signature with the generics and an implementation signature that is not generic and returns a union. While this is not 100% type safe it is better than the type assertion version. 

type Action = 'GREET' | 'ASK'
interface ProperReturn {
  'GREET': { hello: 'Guten Tag!' }
  'ASK': { time: 'Wie spat is es?' }
}
function getUnion<T extends Action>(action: T): ProperReturn[T]
function getUnion(action: Action): ProperReturn[keyof ProperReturn] {
  switch (action) {
    case 'GREET':
      return { hello: 'Guten Tag!' } as const
    case 'ASK':
      return { time: 'Wie spat is es?' } as const
    default:
      throw "WUT";
  }
}
type ProperReturn=T扩展了“问候语”?{hello:'Guten Tag!'}:T扩展'ASK'?{时间:'we spat is es?'}'WUT'
函数getUnionStrict(操作:T):正确返回{ 开关(动作){ 案例“问候”:
//一个选项是使用映射接口,而不是使用条件类型,这使得返回类型更易于遵循。此外,我通常使用单独的实现签名和泛型签名,以及一个非泛型并返回联合的实现签名。虽然这不是100%类型安全的,但比类型断言版本要好

type Action='GREET'|'ASK'
接口属性返回{
'问候':{你好:'古顿标签!'
'问':{时间:'你的口角是什么?}
}
函数getUnion(操作:T):属性返回[T]
函数getUnion(action:action):ProperReturn[keyof ProperReturn]{
开关(动作){
案例“问候”:
以常量返回{hello:'Guten Tag!'}
案例“询问”:
返回{time:'Wie spat is es?}作为常量
违约:
扔“WUT”;
}
}

对于像我这样的搜索者:我提出了两个答案,我认为这两个答案既安全又干净:

类型属性返回={
'问候':{你好:'古顿标签!'
'问':{时间:'你的口角是什么?}
}
//比重新输入“问候”|“询问”要好
类型Action=ProperReturn的键
函数getUnion(操作:T):属性返回[T]{
开关(动作){
案例“问候”:
返回{hello:'Guten Tag!'}作为ProperReturn[T]
案例“询问”:
返回{time:'Wie spat is es?}作为正确返回[T]
违约:
扔“WUT?”
}
}
//t1:{您好:'Guten Tag!';}
常量t1=getUnion('GREET')
//t2:{time:'Wie spat is es?';}
const t2=getUnion('ASK')
没有办法返回
“WUT?”
,对吗?我可能会创建一个类似
接口ProperReturn{GREET:{hello:string};ASK:{time:string}
的类型,并将返回类型设为
ProperReturn[T]
并跳过条件类型和不可能的
“WUT”?
return。但基本上您是对的,您需要自己定义返回类型条件,然后执行断言(或单个调用签名重载,这通常更容易)