首页 > 解决方案 > 在打字稿中,在类型化函数参数中,智能感知会忽略可选属性。为什么?

问题描述

我正在尝试在打字稿中创建一个带有漂亮类型 DX 的路由器初始化程序。

我的目标是允许开发人员在每个级别为每个路由的参数定义路径和参数类型。为了更好地展示我的问题,我的示例被截断。

每个路由节点如下所示:

export type RouteArgs<TProps extends t.Props> =
{
  path: string;
  type?: TProps;
};

为了保留类型,但强制类型约束,我创建了一个“构建器”函数:

export type RoutesArgs = {
  [key: string]: RouteArgs<{}>;
};

interface RoutesFn {
  <TRoutesArgs extends RoutesArgs>(args: TRoutesArgs): TRoutesArgs;
}

export const routes: RoutesFn = args => args;

routes函数的唯一目的是强制输入参数,并返回从参数推断出的实际类型。它是这样使用的:

const r = routes({
  FOO: {
    // Intellisense kicks in here...
  },
});

当智能感知启动FOO对象时,它会同时提示pathtype属性。path是必需的,所以我添加了路径,然后再次点击智能感知。

const r = routes({
  FOO: {
    path: '/foo',
    // Now, intellisense is empty...
  },
});

正如预期的那样type,它现在不推荐任何东西,而不是推荐任何东西。

事实上,它还允许我添加“垃圾”属性......

const r = routes({
  FOO: {
    path: '/foo',
    // Ideally, it would complain about the following...
    rubbish: 1234
  },
});

我担心我遇到了打字稿的结构打字功能。我认为它认为path满意,然后放弃匹配其余部分。

这种方法的部分魅力在于它只是一个对象文字,我不必为每个路由节点实例化类,所以我想避免使用类。

有没有办法说服打字稿在这种情况下强制执行名义打字,而无需为每条路线使用传递功能?

标签: typescript

解决方案


我无法重现您在使用 IntelliSense 时遇到的问题。

至于禁止额外的属性,你是对的,结构类型系统默认允许这样的额外属性。(具体来说,它是interfaceclass扩展所必需的……如果向子类或接口扩展添加属性导致与超类或父接口不兼容,继承和子类型化会很笨拙。)TypeScript 中的对象类型通常不被视为“精确” . 我说“一般”是因为该语言在某些情况下通过过多的属性检查为对象文字开辟了一个例外。但是,在您的情况下,这并不适用,因为对象文字是扩展的泛型类型一个对象类型,由于扩展可能有额外的属性,你又回到了你开始的地方。

所以 TypeScript 不直接支持精确类型。但是您可以通过泛型类型约束间接描述此类类型。例如:

type Exactly<T, C> = T & { [K in Exclude<keyof C, keyof T>]: never };

interface RoutesFn {
  <T extends RoutesArgs>(args: T & { [K in keyof T]: Exactly<RouteArgs<{}>, T[K]> }): T;
}

Exactly<T, C>泛型类型接受一个类型T和一个候选类型,并返回一个新类型,当且仅当它与 的“完全”匹配时才C兼容。如果包含任何额外的属性,这些额外的属性将被转换为 type 。CTCnever

然后RoutesFn类型与之前类似,除了args参数是针对您的受约束泛型T(我已将其从 缩短TRoutesArgs每个属性T都已针对RouteArgs<{}>.

让我们看看它的工作原理:

const r = routes({
  FOO: {
    path: '/foo',
    type: { f: 1 },
    rubbish: 1 // error!
    //~~~~~ <-- Type 'number' is not assignable to type 'never'
  },
  BAR: {
    path: '/bar',
  }
});

所以这很好;您现在会在不丢失类型推断的情况下获得额外属性的错误。还有 IntelliSense,至少在我的 IDE 中(见图


好的,希望有帮助;祝你好运!

Playground 代码链接


推荐阅读