我们如何在泛型函数中关联 TypeScript 中的两个函数参数?

问题描述 投票:0回答:1

我正在努力为函数参数添加正确的类型,其中函数应该是通用的,并且参数应该相互关联。

这是一个最小的可重现示例(在实际代码中,类型

Foo
和对象
objectOne
来自我无法更改的库,而函数
func
是我试图自己创建的函数):

interface Foo {
  union: 'one' | 'two' | 'three';
  object: {
    One: {'oneRelatedKey': 'oneRelatedValue'};
    Two: {'twoRelatedKey': 'twoRelatedValue'};
    Three: {'threeRelatedKey': 'threeRelatedValue'};
  };
};

const objectOne = {
  one: {
    func: (obj: Foo['object']['One']) => {console.log(obj)}
  },
  two: {
    func: (obj: Foo['object']['Two']) => {console.log(obj)}
  },
  three: {
    func: (obj: Foo['object']['Three']) => {console.log(obj)}
  },
};

const func = <T extends Foo['union']>(one: T, two: Foo['object'][Capitalize<T>]) => {
  objectOne[one].func(two);
}

我在

two
:

上遇到错误
Property ''twoRelatedKey'' is missing in type '{ oneRelatedKey: "oneRelatedValue"; }' but required in type '{ twoRelatedKey: "twoRelatedValue"; }'

我想确保使用

func
中的字符串和
Foo['union']
中的相应对象调用
Foo['object']
,并在传入
Foo['union']
参数的大写版本上进行索引。

然后应该可以按如下方式调用

func

func('one', { 'oneRelatedKey': 'foo' })

我尝试了以下方法,看起来更接近:

type FirstTestGeneric<T extends Foo['union']> = T;
type FirstTest = FirstTestGeneric<'one'>; // 'one'

type SecondTestGeneric<T extends Foo['union']> = Capitalize<T> extends keyof Foo['object'] ? Foo['object'][Capitalize<T>] : never;
type SecondTest = SecondTestGeneric<'one'>; // {'oneRelatedKey': string}

const testFunc = <T>(one: FirstTestGeneric<'one'>, two: SecondTestGeneric<'one'>) => {
  objectOne[one].func(two);
}

现在我只需要以某种方式修改

testFunc
以便它使用类型参数。由于某种原因,这不起作用:

const testFunc = <T extends Foo['union']>(one: FirstTestGeneric<T>, two: SecondTestGeneric<T>) => {
  objectOne[one].func(two);
}

我认为问题在于类型

T
接受联合。如果它只接受一个字符串(来自联合
Foo['union']
),它可能会起作用。

javascript typescript
1个回答
0
投票

通过一点点类型操作就可以实现:

interface Foo {
  union: 'one' | 'two' | 'three';
  object: {
    One: {'oneRelatedKey': 'oneRelatedValue'};
    Two: {'twoRelatedKey': 'twoRelatedValue'};
    Three: {'threeRelatedKey': 'threeRelatedValue'};
  };
}

type Temp = Record<Foo['union'], any>

type OneObjType = {
  [K in keyof Temp]: { func: (x: Foo['object'][Capitalize<K>]) => void }
}

const objectOne = {
  one: {
    func: (obj: Foo['object']['One']) => {console.log(obj)}
  },
  two: {
    func: (obj: Foo['object']['Two']) => {console.log(obj)}
  },
  three: {
    func: (obj: Foo['object']['Three']) => {console.log(obj)}
  },
};

const retyped: OneObjType = objectOne; // NOTE: *NOT* unsafe

const func = <T extends Foo['union']>(one: T, two: Foo['object'][Capitalize<T>]) => {
  retyped[one].func(two);
}

注意,你原来的函数是正确的,我没有修改它。诀窍是让编译器接受

objectOne
中方法的参数映射回联合,并且联合连接到
Foo
接口中的相应对象。另请注意,这是对类型的重新解释,而不是强制转换:编译器仍然确保安全。

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.