【问题标题】:Infer type based on input structure根据输入结构推断类型
【发布时间】:2021-05-08 23:17:33
【问题描述】:

我正在努力根据输入参数推断类型。基本上我想使用对象的结构来推断返回类型(尽管不仅仅是 . 我认为诀窍是使用 conditional type 但我无法让它工作:

type Active = { name: string; active: true };
type Inactive = { name: string; active: false };
type IdWithAct = Active | Inactive;

export const getIdWithType = <
  Arg extends IdWithAct,
  T extends Arg extends Active ? 'ActiveItem' : 'InactiveItem'
>({
  name,
  active,
}: IdWithAct): {name: string, typeName: T } =>  ({
    name,
    typeName: active ? 'ActiveItem' : 'InactiveItem',
  });

playground。我查看了infer 解决方案,但这些示例对于我想要完成的(至少我认为)来说太微不足道了......

编辑

添加了来自 cmets 的示例。

重载

正如 cmets 所建议的,有重载选项,不幸的是,TypeScript 似乎在调用另一个函数时遇到了问题:

type ActivityWithName<T> = { name: string, active: T}
type IdWithAct = ActivityWithName<true> | ActivityWithName<false>;

type Types = 'ActiveItem' | 'InactiveItem'
type Ret<T> = { name: string, typeName: Types }

function getFragNameAndIdFromActive(args: ActivityWithName<true>): Ret<'ActiveItem'>
function getFragNameAndIdFromActive(args: ActivityWithName<false>): Ret<'InactiveItem'>
function getFragNameAndIdFromActive<Arg extends IdWithAct>({
  name,
  active,
}: IdWithAct): Ret<Types> {
  return  {
    name,
    typeName: active ? 'ActiveItem' : 'InactiveItem',
  }
}

const retActive = getFragNameAndIdFromActive({ name: 'activeItem', active: true })
const retInactive = getFragNameAndIdFromActive({ name: 'activeItem', active: false })

function test(active: boolean) {
  // active complaints: Type 'boolean' is not assignable to type 'false'.
  const ret = getFragNameAndIdFromActive({ name: 'activeItem', active })
}

playground。该解决方案很有吸引力,因为它对重载的意图相当明确。

运行时断言

运行时断言的建议有效,但在我看来它有点太神奇了,如果返回对象的差异不止一个值,它很可能会变得相当混乱:

type IdWithAct<T> = { name: string; active: T };
type Status<T> = T extends true ? 'ActiveItem' : 'InactiveItem'

export const getIdWithType = <T extends boolean>({
    name,
    active,
}: IdWithAct<T>) => ({
    name,
    typeName: (active ? 'ActiveItem' : 'InactiveItem') as Status<T>,
});

const foo = getIdWithType({ name: 'x', active: true }) // { name: string; typeName: "ActiveItem"; }
const bar = getIdWithType({ name: 'x', active: false }) // { name: string; typeName: "InactiveItem"; }

const test = (active: boolean) => getIdWithType({ name: 'x', active })

【问题讨论】:

标签: typescript typescript-generics


【解决方案1】:

你需要再添加一个重载:


type ActivityWithName<T> = { name: string, active: T}
type IdWithAct = ActivityWithName<boolean>

type Types = 'ActiveItem' | 'InactiveItem'
type Ret<T extends Types> = { name: string, typeName: T } // use generic parameter

function getFragNameAndIdFromActive<T extends true>(args: ActivityWithName<T>): Ret<'ActiveItem'>
function getFragNameAndIdFromActive<T extends false>(args: ActivityWithName<T>): Ret<'InactiveItem'>
function getFragNameAndIdFromActive(args: ActivityWithName<boolean>): Ret<Types> // less specific overload
function getFragNameAndIdFromActive({
  name,
  active,
}: IdWithAct): Ret<Types> {
  return  {
    name,
    typeName: active ? 'ActiveItem' : 'InactiveItem',
  }
}

const retActive = getFragNameAndIdFromActive({ name: 'activeItem', active: true })
const retInactive = getFragNameAndIdFromActive({ name: 'activeItem', active: false })

function test(active: boolean) {
  const ret = getFragNameAndIdFromActive({ name: 'activeItem', active }) // ok
}

Playground

【讨论】:

    猜你喜欢
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2020-12-26
    • 2015-09-18
    相关资源
    最近更新 更多