TypeScrip不理解记录类型必须返回正确类型 [英] Typescript does not understand that record types must return the correct type

查看:0
本文介绍了TypeScrip不理解记录类型必须返回正确类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很抱歉标题很难,但请看以下最低限度的表示:

// We define a couple of symbols
const A: unique symbol = Symbol();
const B: unique symbol = Symbol();

// And create an "enum" of them
const Fs = {
  A,
  B
} as const;

// This is the type A | B
type F = typeof Fs[keyof typeof Fs];

// This is our "input" types
type Foo = {
  [Fs.A]: string,
  [Fs.B]: number,
}

// This is our "output" types
type Bar = {
  [Fs.A]: number,
  [Fs.B]: string,
}

// We define a Mapping for the key (A or B) to be a function taking an
// input of type defined in Foo, and spitting out an output of type defined in Bar
type Mapping<T extends F> = (arg: Foo[T]) => Bar[T];

// We then define a map record as being a list of all such functions
type MapRecord = {
  [key in F]: Mapping<key>
}

// Here is the action mapping. It does indeed comply with map record.
const mappings: MapRecord = {
  [Fs.A]: (arg: string) => 123,
  [Fs.B]: (arg: number) => "asdf",
} as const;

// And here we execute the mapping. Can you spot the error?
function doMapping<T extends F>(t: T, arg: Foo[T]): Bar[T] {
  const mapper: Mapping<T> = mappings[t];
  return mapper(arg);
}

看起来完全没问题。但在const mapper...行上,我们实际上得到以下错误:

Type 'Mapping<unique symbol> | Mapping<unique symbol>' is not assignable to type 'Mapping<T>'.
  Type 'Mapping<unique symbol>' is not assignable to type 'Mapping<T>'.
    Types of parameters 'arg' and 'arg' are incompatible.
      Type 'Foo[T]' is not assignable to type 'string'.
        Type 'string | number' is not assignable to type 'string'.
          Type 'number' is not assignable to type 'string'.

它似乎认为mapping[t]的结果可能是任何可能的记录,而我们知道它必须只是一个。

为什么?另外,我如何才能让打字稿信服呢?

推荐答案

附注:

首先:

const mappings: MapRecord = {
  [Fs.A]: (arg: string) => 123,
  [Fs.B]: (arg: number) => "asdf",
} as const;
不要将显式类型MapRecordas const一起使用。mappings将被推断为MapRecordas const根本不影响mappings的类型。因此,您需要应用一个或另一个机器人,而不是两个都应用。

秒:

主要问题在这一行:mappings[t]mappings[t]返回函数并集。当您想要调用函数的联合时,它们的参数是相交的。因此,您将获得number & string === never。请参阅docs

同样,同一类型变量在逆变量位置的多个候选项会导致推断交集类型:

请参阅相关answerarticlearticle


为了推断映射并允许它,您还应该将mappings作为参数传递。我通常curry它。

您可以使用此方法:

// We define a couple of symbols
const A: unique symbol = Symbol();
const B: unique symbol = Symbol();

// And create an "enum" of them
const Fs = {
    A,
    B
} as const;


// This is our "input" types
type Foo = {
    [Fs.A]: string,
    [Fs.B]: number,
}

// This is our "output" types
type Bar = {
    [Fs.A]: number,
    [Fs.B]: string,
}

// Here is the action mapping. It does indeed comply with map record.
const mappings = {
    [Fs.A]: (arg: string) => 123,
    [Fs.B]: (arg: number) => "asdf",
} as const;


// This is the type A | B
type F = typeof Fs[keyof typeof Fs];

const doMapping = <
    Key extends symbol,
    Value extends (arg: any) => any, // MAIN DRAWBACK
    Mapping extends Record<Key, Value>
>(mapping: Mapping) =>
    <
        Type extends keyof Mapping,
        >(type: Type, arg: Parameters<Mapping[Type]>[0]): ReturnType<Mapping[Type]> =>
        mapping[type](arg)

const result = doMapping(mappings)(Fs.A, 's') // number
const result2 = doMapping(mappings)(Fs.B, 42) // number

const error = doMapping(mappings)(Fs.B, 'a') // expected error

Playground

Key-是主参数的推论关键 Value-是推断函数 Mapping-是推断参数。

您可能已经注意到,这类似于我们如何分解JS对象。以similar方式可以推断它们。算法是:推断每个嵌套的键和值,然后将它们组装成一个数据结构。

Type-使用适当的约束推断第一个参数

您可以在我的blog

中找到有关函数参数推理的详细信息

我个人认为最安全的方法是返回一个函数:

// We define a couple of symbols
const A: unique symbol = Symbol();
const B: unique symbol = Symbol();

// And create an "enum" of them
const Fs = {
    A,
    B
} as const;


// This is our "input" types
type Foo = {
    [Fs.A]: string,
    [Fs.B]: number,
}

// This is our "output" types
type Bar = {
    [Fs.A]: number,
    [Fs.B]: string,
}

// Here is the action mapping. It does indeed comply with map record.
const mappings = {
    [Fs.A]: (arg: string) => 123,
    [Fs.B]: (arg: number) => "asdf",
} as const;

type Mappings = typeof mappings

const doMapping = <Type extends keyof Mappings,>(type: Type) =>
    mappings[type]

const result = doMapping(Fs.A) // (arg:string)=>number

Playground

轻松简单。

这篇关于TypeScrip不理解记录类型必须返回正确类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆