在打字稿中键入通用键值接口 [英] Typed Generic Key Value Interface in Typescript

查看:51
本文介绍了在打字稿中键入通用键值接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下示例对象:

let foo: Foo = {
  'key1': { default: 'foo', fn: (val:string) => val },
  'key2': { default: 42, fn: (val:number) => val },

  // this should throw an error, because type of default and fn don't match
  'key3': { default: true, fn: (val:string) => val }
}

界面应如下所示:

interface Foo {
  [key: string]: { default: T, fn: (val:T) => any }
}

这当然是行不通的,因为没有定义 T .

This of course doesn't work, because there's no T defined.

所以我考虑过这样做:

interface FooValue<T> {
  default: T;
  fn: (val:T) => any;
}

interface Foo {
  [key: string]: FooValue<?>
}

但是我也被困住了.因为我无法定义 FooValue 的通用类型.

But there I got stuck, too. Because I can't define the generic type of FooValue.

如果我使用 FooValue< any> ,那么当然所有内容都将键入 any .虽然那行不通.

If I use FooValue<any> then of course everything is typed as any. Though that doesn't work.

我想确保 default 的类型和 fn 的参数类型始终相同.

I want to ensure that the type of default and the parameter type of fn are always the same.

有什么解决办法吗?还是不能做到这一点?

Is there any solution? Or can't this be done?

推荐答案

如何将 Foo< T> 定义为

How about defining Foo<T> to be a mapped type, like this:

interface FooValue<T> {
  default: T;
  fn: (val: T) => any;
}

type Foo<T> = {
  [K in keyof T]: FooValue<T[K]>
}

在这种情况下,如果 T 是某种普通对象类型,例如 {a:字符串,b:数字,c:布尔值} ,则 Foo< T> 是其 Foo 的版本: {a:FooValue< string>,b:FooValue< number>,c:FooValue< boolean&}.现在,您可以创建一个辅助函数,该函数仅在某些类型的 T 可以被推断为 Foo< T> 的情况下才接受对象文字.

In this case, if T is some normal object type like {a: string, b: number, c: boolean}, then Foo<T> is the Foo-ized version of it: {a: FooValue<string>, b: FooValue<number>, c: FooValue<boolean>}. Now you can make a helper function which accepts an object literal only if it can be inferred as a Foo<T> for some type T:

function asFoo<T>(foo: Foo<T>): Foo<T> {
  return foo;
}

此功能有效是因为TypeScript编译器可以执行从映射类型推断,从而允许其从 Foo< T> 推断 T .工作正常了:

This function works because the TypeScript compiler can do inference from mapped types, allowing it to infer T from Foo<T>. Here is it working:

let foo = asFoo({
  key1: { default: 'foo', fn: (val: string) => val },
  key2: { default: 42, fn: (val: number) => val }
});
// inferred as { key1: FooValue<string>; key2: FooValue<number>;}

这是失败的地方:

let badFoo = asFoo(
  key1: { default: 'foo', fn: (val: string) => val },
  key2: { default: 42, fn: (val: number) => val },
  key3: { default: true, fn: (val: string) => val }
}); 
// error! Types of property 'key3' are incompatible. 
// Type 'boolean' is not assignable to type 'string'.

希望有帮助.祝你好运!

Hope that helps. Good luck!

更新:由于 FooValue<因为 foo.key1.fn('abc')被推断为 any 类型,因此上述代码假定您还可以.字符串> ['fn'] 被定义为返回 any 的函数.有点忘记原始对象文字的输出类型.如果希望 foo 记住其属性的 fn 方法的返回类型,则可以执行以下稍微不同的帮助函数:

Update: The above code assumes you're okay with foo.key1.fn('abc') being inferred as type any, since FooValue<string>['fn'] is defined as a function that returns any. It kind of forgets the output type from the original object literal. If you want foo to remember the return type of its properties' fn methods, you can do this slightly different helper function:

function asFoo<T, F>(foo: F & Foo<T>): F {
  return foo;
}

let foo = asFoo({
  key1: { default: 'foo', fn: (val: string) => val },
  key2: { default: 42, fn: (val: number) => val },
  // next line would cause error
  // key3: { default: true, fn: (val: string)=>val} 
})

const key1fnOut = foo.key1.fn('s') // known to be string
const key2fnOut = foo.key2.fn(123) // known to be number

那行得通.在这种情况下, asFoo()只是验证输入是否是某些 T Foo< T> ,但不会强制输入输出类型为 Foo< T> .根据您的用例,您可能更喜欢此解决方案.再次祝你好运.

And that works. In this case, asFoo() just verifies that the input is a Foo<T> for some T, but it doesn't coerce the output type to a Foo<T>. Depending on your use cases, you may prefer this solution to the other one. Good luck again.

这篇关于在打字稿中键入通用键值接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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