【问题标题】:typescript cannot infer subset of keys of object from usage打字稿不能从使用中推断出对象的键子集
【发布时间】:2018-09-07 19:22:51
【问题描述】:

我正在研究 ts 中的代数数据类型,并坚持键入 match 函数。问题的简化版本如下所示:

let rec = { a: 'a', b: 1 };

// just captures type of 'rec' and then reuses the type for eval
const makeEval = <Record>(rec: Record) => <
  Res,
  K extends keyof Record = keyof Record
>(
  val: Record,
  cases:
    | Cases<Record, Res>
    | (Cases<Record, Res, K> & { else: (r: Record) => Res })
): Res => (undefined as any) as Res;

export type Cases<Record, Res, K extends keyof Record = keyof Record> = {
  [T in K]: (value: Record[T]) => Res
};

const evalMyRecord = makeEval(rec);

// this is fine
const a = evalMyRecord(rec, { a: s => s, b: n => n.toString() });

// err
const b = evalMyRecord(rec, { a: s => s, else: _ => 'why err?' });
// Property 'b' is missing in type '{ a: (s: string) => string; else: (_: { a: string; b: number; }) => string; }'.

// requires explicit subset
const b_ = evalMyRecord<string, 'a'>(rec, {
  a: s => s,
  else: _ => 'but explicit a is fine'
});

// full set is fine
const c = evalMyRecord(rec, {
  a: s => s,
  b: n => 'n',
  else: _ => 'fine too'
});

所以我想表达一种类型,它要么具有 Record 的所有键,要么具有它的任何子集 + {else} case。

我知道 Partial 有一个解决方案:

type EvalCases<Record, Res> =
  | Cases<Record, Res>
  | (Partial<Cases<Record, Res>> & { else: (r: Record) => Res });

const b = evalMyRecord(rec, { a: s => s, else: _ => 'works' });

const b2 = evalMyRecord(rec, {
  a: s => s,
  b: undefined,
  else: _ => 'also works but weird'
});

但是有: {b: undefined} 看起来有点不对劲。如果任何情况都不是正确的函数(如果可能的话),我想要一个编译器错误。

关于打字稿魔法有什么建议吗?

注意:概念上接近(但不一样)Typescript cannot infer correct argument types for an object of functions

【问题讨论】:

  • 关于 “但是有:{b: undefined} 看起来有点不对劲。”,部分意味着你可以完全省略属性。它创建了一个所有属性都是可选的类型
  • 是的。这是一个合适的解决方法,但我更喜欢 {b: undefined} 出现编译器错误。所以它要么是适当的功能,要么根本就没有道具

标签: typescript type-inference


【解决方案1】:

以下解决方案允许您拥有Cases&lt;Record, Res&gt;Cases&lt;Record, Res&gt; 的任何子集以及else 属性。

type EnforcingPartial<T> = {
    [key in keyof T]: { [subKey in key]: T[key]; }
}[keyof T];

type EvalCases<Record, Res> = Cases<Record, Res> |
    (EnforcingPartial<Cases<Record, Res>> & { else: (r: Record) => Res });

const b = evalMyRecord(rec, { a: s => s, else: _ => 'works' });

const b2 = evalMyRecord(rec, {
    a: s => s,
    b: undefined, // ERROR HERE, b is incompatible
    else: _ => 'also works but weird'
});

【讨论】:

  • 这真的很酷。但是有一个失败的案例typescript const c = evalMyRecord(rec, { a: s =&gt; s, b: n =&gt; 1, else: _ =&gt; 'fine too' }); n=> 1 没有检查返回类型(应该是字符串)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-27
  • 2021-09-13
  • 2022-07-30
  • 1970-01-01
  • 1970-01-01
  • 2018-09-19
  • 2021-06-08
相关资源
最近更新 更多