我有以下示例对象:

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
所以我想这样做:
interface FooValue<T> {
  default: T;
  fn: (val:T) => any;
}

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

但我也被卡住了。因为我无法定义FooValue的泛型类型。
如果我使用FooValue<any>的话,当然所有的东西都被输入为any。但那不管用。
我想确保default的类型和fn的参数类型总是相同的。
有什么解决办法吗?还是做不到?

最佳答案

Foo<T>定义为mapped type怎么样,如下所示:

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

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

在这种情况下,如果T是像{a: string, b: number, c: boolean}这样的普通对象类型,那么Foo<T>是它的Foo化版本:{a: FooValue<string>, b: FooValue<number>, c: FooValue<boolean>}。现在,您可以创建一个helper函数,该函数仅在对象文本可以被推断为某个类型的Foo<T>时才接受它:
function asFoo<T>(foo: Foo<T>): Foo<T> {
  return foo;
}

此函数之所以起作用,是因为typescript编译器可以执行inference from mapped types,从而允许它从T中推断T。它的工作原理如下:
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'.

希望能有所帮助。祝你好运!
更新:以上代码假设您可以将Foo<T>推断为类型foo.key1.fn('abc'),因为any被定义为返回FooValue<string>['fn']的函数。它有点忘记了原始对象文本的输出类型。如果希望any记住其属性'foo方法的返回类型,则可以执行稍微不同的helper函数:
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

这很管用。在这种情况下,fn只验证对于某些asFoo()输入是Foo<T>,但它不会将输出类型强制为T。根据您的用例,您可能更喜欢这个解决方案而不是另一个。祝你好运。

关于typescript - typescript 中键入通用键值接口(interface),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49562622/

10-09 09:09