繁体   English   中英

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

[英]typescript: how to return proper type from generic function constrained by lookup type generic returning union

所以假设我们有以下功能实现:

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?'
  }
}

此函数的返回类型如下联合:

{"WUT?" | { hello: 'Guten Tag!'; time?: undefined; } | { time: 'Wie spat is es?'; hello?: undefined; }}

所以我们可能会认为,如果我们通过 switch-case 中使用的“判别联合”类型使用泛型的函数约束,它将返回该特定分支类型,如下所示:

// 🚨 NOPE !
// $ExpectType  {time: 'Wie spat is es?'}
const t1 = getUnion('ASK')

不幸的是,这是错误的假设,因为我们得到的是整个联合而不是一种缩小的类型

// ✅$ExpectType {"WUT?" | { hello: 'Guten Tag!'; time?: undefined; } | { time: 'Wie spat is es?'; hello?: undefined; }}
const t1 = getUnion('ASK')

这是正确的行为还是编译器限制?

无论如何,这如何解决?

所以const t1 = getUnion('ASK')将返回{time: 'Wie spat is es?'}

所以我想出了以下内容:

此实现减轻了以前的问题,因为它通过条件类型映射器通过使用的函数参数从返回联合正确返回缩小类型:

type ProperReturn<T> = T extends 'GREET' ? {hello:'Guten Tag!'} : T extends 'ASK' ? {time:'Wie spat is es?'} : 'WUT'
function getUnionStrict<T extends Action>(action: T): ProperReturn<T> {
  switch (action) {
  case 'GREET':
    // 🍿it needs to be explicitly casted, which is OK I guess?
    return {hello: 'Guten Tag!' } as ProperReturn<T>
  case 'ASK':
    // 🍿 Cast needed
    return {time:'Wie spat is es?'} as ProperReturn<T>
  default:
    // 🍿 Cast needed
    return 'WUT?' as ProperReturn<T>
  }
}
// ✅ exactly what we wanted 
// $ExpectType {hello:'Guten Tag!'}
const t11 = getUnionStrict('ASK')

// ✅
// $ExpectType {time:'Wie spat is es?'}
const t22 = getUnionStrict('GREET')

一种选择是使用映射接口而不是使用条件类型,使返回类型更易于遵循。 此外,我通常使用带有泛型的单独实现签名和非泛型并返回联合的实现签名。 虽然这不是 100% 类型安全,但它比类型断言版本更好。

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 = {
  'GREET': { hello: 'Guten Tag!' }
  'ASK': { time: 'Wie spat is es?' }
}

// better than retyping 'GREET' | 'ASK'
type Action = keyof ProperReturn

function getUnion<T extends Action>(action: T): ProperReturn[T] {
  switch (action) {
  case 'GREET':
    return {hello: 'Guten Tag!' } as ProperReturn[T]
  case 'ASK':
    return {time:'Wie spat is es?'} as ProperReturn[T]
  default:
    throw "WUT?"
  }
}

// t1: { hello: 'Guten Tag!'; }
const t1 = getUnion('GREET')

// t2: { time: 'Wie spat is es?'; }
const t2 = getUnion('ASK')

游乐场链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM