Typescript使第二个参数类型依赖于第一个参数类型

zvokhttg  于 2023-01-06  发布在  TypeScript
关注(0)|答案(4)|浏览(201)
enum Channel {
  GITHUG = 'github',
  GITLAB = 'gitlab',
}

interface Github {}

interface Gitlab {}

function foo (a, b) {}

第一个参数a是枚举(通道)值
如果参数a为github,则b的类型为GitHub
如果参数a为gitlab,则b的类型为Gitlab
如何定义函数foo的类型?

czfnxgou

czfnxgou1#

这里有一些替代方法:

enum Channel {
  GITHUB = 'github',
  GITLAB = 'gitlab',
}

interface Github {
  type: 'Github'
}

interface Gitlab {
  type: 'Gitlab'
}

/**
 * First approach
 */
type Params = [Channel.GITHUB, Github] | [Channel.GITLAB, Gitlab]

function foo(...args: Params) {
  if (args[0] === Channel.GITHUB) {
    const x = args[1] // Github
  }
}

type Overloading =
  & ((a: Channel.GITHUB, b: Github) => void)
  & ((a: Channel.GITLAB, b: Gitlab) => void)

/**
 * Second approach
 */

const foo2: Overloading = (a: Channel, b: Github | Gitlab) => null as any

const result = foo2(Channel.GITHUB, { type: 'Github' }) // ok

/**
 * Third approach
 */
function foo3(a: Channel.GITLAB, b: Gitlab): void
function foo3(a: Channel.GITHUB, b: Github): void
function foo3(a: Channel, b: Github | Gitlab) {

}
foo3(Channel.GITLAB, { type: 'Gitlab' }) // ok

Playground

qjp7pelc

qjp7pelc2#

使用conditional types technique。查看下面的示例或转到在线Playground以进行实际测试(感谢Joonas的帮助)

enum Channel {
  GITHUG = 'github',
  GITLAB = 'gitlab',
}

interface Github {
  _foo: number
}

interface Gitlab {
  _bar: number
}

type ParamType<C> = C extends Channel.GITHUG ? Github : Gitlab;
function foo<C extends Channel>(a: C, b: ParamType<C>): void {}

foo(Channel.GITLAB, { _foo: 1 }); // error
foo(Channel.GITLAB, { _bar: 1 }); // success

拜托,让我知道它是否有效)

mwkjh3gx

mwkjh3gx3#

请看代码注解:

enum Channel {
  GITHUG = 'github',
  GITLAB = 'gitlab',
}

interface Github {}

interface Gitlab {}

// Map enum value and type
interface Foo {
  github: Github
  gitlab: Gitlab
}

// `${Channel}` => 'github' | 'gitlab'
// Get the type K of a through the passed parameter
// if K is `github`, Foo[K] is Github
// if k is `gitlab`, Foo[K] is Gitlab
function foo<K extends `${Channel}`>(a: K, b: Foo[K]) {}

Playground

dgiusagp

dgiusagp4#

我遇到了一个非常相似的问题,在我的例子中,第二个参数类型来自一个接口:

interface ArgTypes { [id: string]: any }
interface ChannelArgs extends ArgTypes{
  github: {
    hubhub: number
  },
  gitlab: {
    lablab: number
  },
  ...
}
type Arg<Args extends ArgTypes, Id extends keyof Args> = Args[Id]

我的foo函数必须与实现接口的所有类型一起工作,因此它本身是泛型的:

foo<ChannelArgs>('github', {hubhub: 12})

事实证明,泛型类型和推断类型的声明不能混合,但可以将它们放在不同的括号中:

type Foo<Args extends ArgTypes> = <Id extends keyof Args>(
  id: Id,
  arg: Arg<Args, Id>
) => void

const gitFoo: Foo<ChannelArgs> = function(a,b): void {}

gitFoo('github', {hubhub: 12}) // no error
gitFoo('github', {lablab: 12}) // error

相关问题