根据参数中的嵌套键返回的函数的打字稿定义

时间:2019-07-02 13:06:34

标签: typescript typescript-typings

我正在尝试为我们正在使用的库编写声明文件,而没有任何修改方法。

它的工作方式是您可以发送配置对象,返回将基于其中的一些键

const args = {
  // ...
  resources: {
    One: {
      query: { url: 'value' }
    }
  } 
}

library(args)

调用它会返回一个对象,该对象具有resources的深层嵌套键作为函数

const ret = {
  One: {
    query: async (query) => <data>
 }
}

// ret.One.query(args) => data

理想情况下,每个<data>也会被键入,由于动态键,我不确定是否可以输入?我已经尝试了几种使用keyof参数的方法,但没有任何运气

编辑:更新示例

const config = {
  // ...
  resources: {
    User: {
      find: { url: 'http://example.com/userapi' }
    },
    Dashboard: {
      update: { method: 'post', url: 'http://example.com/dashboardapi'}
    }
  } 
}

const services = serviceCreator(config)

// service creator turns the supplied resources object into promise functions

await services.User.find({id: '123'}) // Promise<User>
await services.Dashboard.update({user: '123', action: 'save'}) // Promise<Dashboard>

2 个答案:

答案 0 :(得分:2)

没有更多信息,我猜您正在寻找类似这样的东西:

type Arg<T> = { resources: T; }
type Ret<T> = {
    [K in keyof T]: {
        query: (...query: any[]) => Promise<any>;
    }
}
declare const library: <T>(arg: Arg<T>) => Ret<T>;

让我们测试一下是否有效:

const args = {
    resources: {
        One: {
            query: { url: 'value' }
        }
    }
}
var ret = library(args);

async function test() {
    await ret.One.query();  // OK
}

您可以在此playground中进行尝试。

答案 1 :(得分:1)

我对TS还是陌生的,发现这个问题真的很有趣。这是我的看法,它很冗长,需要 weird 类型输入。但是它得到了很好的类型建议。我希望有更多经验丰富的用户提供反馈:

/**
 * map dynamic resource type to method name,
 * i.e 'User' -> 'find' | 'lookup'
 */
type MethodMap = Record<string, string>

interface ResourceParams {
  url: string;
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
}

/**
 * Each method in resource type has interface ResourceParams
 */
type Resources<T extends MethodMap> = {
  [K in keyof T]: {
    [S in T[K]]: ResourceParams;
  }
}


interface Config<T extends MethodMap> {
  resources: Resources<T>;
  [key: string]: unknown;
}

type Result<T extends MethodMap> = {
  [K in keyof T]: {
    [S in T[K]]: () => Promise<any>
  }
}

declare const library: <T extends MethodMap>(config: Config<T>) => Result<T>

// usage

const result = library<{ User: 'find' | 'lookup'; Dashboard: 'search' }>({
  resources: {
    User: {
      find: { url: 'value' },
      lookup: { url: 'asdads', method: 'GET' },
    },
    Dashboard: {
      search: { url: 'value' }
    },
  } 
})

result.User.find() // OK
result.User.lookup() // OK
result.Dashboard.search() // OK

result.User.search() // Not OK
result.Dashboard.find() // Not OK
result.Store.find() // Not OK

Link to playground