首页 > 解决方案 > 将 TypeScript 类型限制为仅允许的键,即 Exact/DeepExact

问题描述

这是对这个 SO question的后续尝试和模仿Exact type

我想我有一些接近DeepExact工作的东西:

// This is the good type/that we want to only accept Exact versions of
type Opts = {
  firstName?: string;
  parent?: string;
  children?: { firstName?: string }[];
};

// This is the incoming type that has unknown fields which should be rejected.
type Args = {
  firstName?: string;
  parentId?: string;
  children?: { firstName?: string; unknownField?: string }[];
};

const a: Args = null!;
// This compiles as expected but I want parentId and children.unknownField to cause type failures
const o: Opts = a;

// function that enforces only the exact Opts type
function onlyOpts<O>(o: Exact<Opts, O>): void {}

// good examples, using known fields
const g1 = { firstName: "f" };
onlyOpts(g1);
const g2 = { parent: "p", children: [{ firstName: "c1" }] };
onlyOpts(g2);

// bad examples, using unknown fields
const b1 = { firstName: "f", parentId: undefined };
onlyOpts(b1); // correctly fails b/c parentId is `never`

const b2 = { firstName: "f", children: [{ firstName: "c1", unknownField: "c1" }] };
onlyOpts(b2); // does not fail but should, unknownField is still `string`

// type alias to show B2["children"][]["unknownField"] is still string
type B2 = Exact<Opts, typeof b2>;

// Prototype Exact/DeepExact
type Exact<T, U> = T &
  {
    [K in keyof U]: K extends keyof T
      ? T[K] extends Array<infer TU>
        ? U[K] extends Array<infer UU>
          ? Array<Exact<TU, UU>>
          : never
        : U[K]
      : never;
  };

但我的b2例子并没有失败,即深度/递归部分。该B2类型仍然children 有 withunknownField: string而不是unknownField: never.

我认为单独推断TUand UU,然后将它们递归到Exact会起作用,但由于某种原因它不是。

有任何想法吗?

标签: typescriptmapped-types

解决方案


这目前适用于我的大多数琐碎测试用例:

export type Exact<T, U> = T &
  {
    [K in keyof U]: K extends keyof T
      ? T[K] extends Array<infer TU> | undefined | null
        ? U[K] extends Array<infer UU> | undefined | null
          ? Array<Exact<TU, UU>> | undefined | null
          : never
        : U[K]
      : never;
  };

但是在具有更深奥的泛型/用法/等的更大应用程序中失败了。所以仍在努力,但我基本上已经回答了我原来的问题。


推荐阅读