首页 > 解决方案 > 打字稿:如何创建具有 2 项的元组,其中第 2 项使用第 1 项的值作为键?

问题描述

我想创建一个数组元组,其值 ['account', 'accountOne'] 使用现有类型,但第二个元组值应包含基于第一个选择的值。

下面的一些示例代码:

interface RootState {
  account: Account;
  movies: Movies;
  config: Config;
}
 
interface Account {
  accountOne: 'something',
  accountTwo: '1'
}

interface Movies {
  moviesOne: 'something',
  moviesTwo: 'something' 
}

interface Config {
  configOne: 'something',
  configTwo: '123' 
}

export type ModuleProp = keyof RootState;
  

// This is some pseudo code, to show the idea, but I could not make it work    
// It gives error: Tuple type arguments circularly reference themselves
export type ModulesTuple = [ModuleProp, keyof RootState[ModulesTuple[0]]
    
// It would be used as
function fetchSomething({page: number, vuexModuleProp: ModulesTuple}){

  const [module, moduleProp] = vuexModuleProp
  // rest of the code
}

fetchSomething({
  page: 1,
  vuexModuleProp: ['movies', 'accountOne'] // ERROR
})

fetchSomething({
  page: 1,
  vuexModuleProp: ['movies', 'moviesOne'] // GOOD TO GO
})

当前代码给出以下错误:元组类型参数循环引用自身

这个想法是,如果你选择'account'作为第一个元组,第二个选择应该是'accountOne'或'accountTwo',它们是RootState接口中其他接口(Movies,Account,Config)的嵌套键。

不确定 Typescript 是否可行,但非常感谢任何帮助!

标签: javascriptarraystypescripttuples

解决方案


使用泛型

interface RootState {
  account: Account;
  movies: Movies;
  config: Config;
}
 
interface Account {
  accountOne: 'something',
  accountTwo: '1'
}

interface Movies {
  moviesOne: 'something',
  moviesTwo: 'something' 
}

interface Config {
  configOne: 'something',
  configTwo: '123' 
}

// All available modules
type ModuleKeys = keyof RootState;

// Pick a module interface based on type of `T`
type ModuleProps<T extends ModuleKeys> = T extends 'account'
  ? Account
  : T extends 'movies'
  ? Movies
  : T extends 'config'
  ? Config
  : never;

// Creates a tuple where the second type is based on the first type, where the first type is automatically inferred from `T`.
type ModuleTuple<T extends ModuleKeys> = [T, keyof ModuleProps<T>];

// Guard the `T` type to be one of `ModuleKeys`, where it is passed to `ModuleTuple<T>`, where it creates the conditional tuple.
function fetchSomething<T extends ModuleKeys>({ page, vuexModuleProp }: { page: number, vuexModuleProp: ModuleTuple<T> }): any {
  const [moduleKey, moduleProps] = vuexModuleProp;
  console.log(page, moduleKey, moduleProps);
}

fetchSomething({
  page: 1,
  vuexModuleProp: ['movies', 'accountOne']  // ERROR
});

fetchSomething({
  page: 1,
  vuexModuleProp: ['movies', 'moviesOne']   // GOOD TO GO
});

检查操场


推荐阅读