如何为高阶函数基于静态字符串值推断类型?

时间:2019-02-13 19:50:04

标签: typescript types

如何将静态字符串值(用于集合名称)映射到特定的返回类型?

type Apple = {
  color: string
  taste: string
}

type Banana = {
  color: string
  length: string
}

type Carrot = {
  color: string
  baby: boolean
}

function collection(collectionName: string) {
  return function get() {
    switch (collectionName) {
      case 'apples':
        return [{
          color: 'red',
          taste: 'great',
        }];

      case 'bananas':
        return [{
          color: 'yellow',
          length: 'medium',
        }];

      case 'carrots':
        return [{
          color: 'orange',
          baby: false,
        }];
    }
  }
}

例如,在使用中,我希望它推断特定的集合名称("apples")将返回特定类型的数组(Apple)。

collection('apples')()  // => Apple[]

3 个答案:

答案 0 :(得分:2)

@NurbolAlpysbayev的回答很好,并且会超载。

或者,我通常尝试使用generics代替,因为它们倾向于与联合更好地结合。例如,您可以创建一个从水果名称到水果类型的映射:

interface FruitMap {
  apples: Apple,
  bananas: Banana,
  carrots: Carrot
}

并使用keyof and lookup types描述collectionNamecollection()的输出之间的关系:

function collection<K extends keyof FruitMap>(collectionName: K): () => Array<FruitMap[K]>;
function collection(collectionName: keyof FruitMap): () => Array<FruitMap[keyof FruitMap]> {
  return function get() {
    switch (collectionName) {
      case 'apples':
        return [{
          color: 'red',
          taste: 'great',
        }];

      case 'bananas':
        return [{
          color: 'yellow',
          length: 'medium',
        }];

      case 'carrots':
        return [{
          color: 'orange',
          baby: false,
        }];
    }
  }
}

这会导致它们正确键入:

const appleArray = collection("apples")(); 
// const appleArray: Apple[]

const appleOrBananaArray = collection(Math.random() < 0.5 ? "apples" : "bananas")();
// const appleOrBananaArray: (Apply | Banana)[]

希望有所帮助;祝你好运!

答案 1 :(得分:1)

Overloads非常适合您的用例。

function collection(collectionName: 'bananas'): () => Banana[]
function collection(collectionName: 'carrots'): () => Carrot[]
function collection(collectionName: 'apples'): () => Apple[]
function collection(collectionName: string) {/*... */ }

let v = collection('apples')() // Apple[]

答案 2 :(得分:0)

从答案的组合来看,在我的用例中,在接口声明中使用重载似乎是最优雅的解决方案。

type Apple = {
  color: string
  taste: string
}

type Banana = {
  color: string
  length: string
}

type Carrot = {
  color: string
  baby: boolean
}

interface TypedCollectionFactory {
    (collectionName: 'apples'): () => Apple[]
    (collectionName: 'bananas'): () => Banana[]
    (collectionName: 'carrots'): () => Carrot[]
}

const collection: TypedCollectionFactory = function(collectionName: string) {
  return function get() {
    // ...
  }
}

const apples = collection('apples')()

for (const apple of apples) {
    console.log(apple.taste)
}