【问题标题】:Api config typing with generics使用泛型输入 Api 配置
【发布时间】:2021-02-15 17:50:47
【问题描述】:

我正在尝试强制键入后端处理,但在泛型如何与 ...args 交互时遇到问题。

以下是带有声明和 2 个用例的代码示例。案例之间的唯一区别是在第一种情况下明确设置了配置类型。

问题

  • 在第一种情况下 - 配置输入良好,但实例输入缺乏
  • 在第二种情况下 - 配置输入缺少,但实例输入良好

理想情况下,我希望 良好 输入配置和服务。

我尝试了Parameters<typeof someFunction>,它可以正常工作,只有泛型有时会失败。

检查TS playground,以便您了解它是如何工作的(您可以将鼠标悬停以检查类型等)。 TS 版本为4.1.3

提前感谢您的帮助。

/************** DECLARATION ******************/

interface EndpointConfig {
  in: (...args: any) => void
  out: (value: 'correct') => void
}
type EndpointConfigs = Record<string, EndpointConfig>

class ApiService<ENDPOINT_CONFIGS extends EndpointConfigs> {
  constructor( public endpointConfigs: ENDPOINT_CONFIGS ) {}

  endpoint <ENDPOINT extends keyof ENDPOINT_CONFIGS>(endpoint: ENDPOINT): ApiEndpoint<ENDPOINT_CONFIGS, ENDPOINT> {
    return new ApiEndpoint( endpoint )
  }
}

class ApiEndpoint<ENDPOINT_CONFIGS extends EndpointConfigs, ENDPOINT extends keyof ENDPOINT_CONFIGS> {
  constructor( public endpoint: ENDPOINT ) {}

  async run (...args: Parameters<ENDPOINT_CONFIGS[ENDPOINT]['in']>): Promise<any> {}
}

/************** CASE 1 *******************/

const endpointConfigs1: EndpointConfigs = {
  testEndpoint: {
    in: (value: 'correct') => {},
    out: (value) => { value++ } // <- Complains here. Which is correct.
  }
};

const apiServiceInstance1 = new ApiService(endpointConfigs1)
const testEndpoint1 = apiServiceInstance1.endpoint('testEndpoint')
testEndpoint1.run('wrong') // <- Doesn't complain here. Which is NOT what we want.


/************** CASE 2 *******************/

const endpointConfigs2 = {
  testEndpoint: {
    in: (value: 'correct') => 'wrong',
    out: (value) => { value++ } // <- value is considered `any` here. Which is NOT what we want.
  }
};

const apiServiceInstance2 = new ApiService(endpointConfigs2)
const testEndpoint2 = apiServiceInstance2.endpoint('testEndpoint')
testEndpoint2.run('wrong') // <- Complains here, which is what we want.

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

    可以通过一个小技巧让 TS 推断出某些类型。这是一个可以在各种情况下应用的最小工作示例:

    const trick = <K extends string>(k: K) => k
    // has type "test"
    const test = trick("test")
    

    让我们看看我们如何在您的上下文中应用它 (Playground link):

    interface EndpointConfig<Args extends any[]> {
      in: (...args: Args) => void
      out: (value: 'correct') => void
    }
    
    
    type ConfigDeclaration<P extends Record<string, any[]>> = {
        [k in keyof P]: EndpointConfig<P[k]>
    }
    
    
    type InType<Cfg extends EndpointConfig<any>> =
      Cfg extends EndpointConfig<infer Args>
        ? Args
        : never
    
    
    // This is a trick to infer the correct type.
    // I think the JIT compiler should take care of the runtime overhead
    const trick = <P extends Record<string, any[]>>(
      declaration: ConfigDeclaration<P>
      ): ConfigDeclaration<P> =>
        declaration
    
    
    // Here, we apply the trick
    const endpointConfigs = trick({
      testEndpoint: {
        in: (value: "correct") => 'wrong',
        // The type of `value` is inferred as intended.
        out: value => {  } 
      }
    })
    
    
    // The type is as intended
    type Test = {
      [k in keyof typeof endpointConfigs]: InType<typeof endpointConfigs[k]>
    }
    
    
    class ApiService<D extends ConfigDeclaration<any>> {
      constructor( public endpointConfigs: D ) {}
    
      endpoint <ENDPOINT extends keyof D>(endpoint: ENDPOINT): ApiEndpoint<D, ENDPOINT> {
        return new ApiEndpoint( endpoint )
      }
    }
    
    
    class ApiEndpoint<D extends ConfigDeclaration<any>, ENDPOINT extends keyof D> {
      constructor( public endpoint: ENDPOINT ) {}
    
      async run (...args: InType<D[ENDPOINT]>): Promise<any> {}
    }
    
    
    const apiServiceInstance2 = new ApiService(endpointConfigs)
    const testEndpoint2 = apiServiceInstance2.endpoint('testEndpoint')
    
    
    testEndpoint2.run('wrong') // <- Complains here, which is what we want.
    testEndpoint2.run("correct")
    

    【讨论】:

    • 完美运行,谢谢!您是自己想出窍门的,还是浓缩在我错过的某个文档中。如果是的话,链接会很棒,因为我真的不明白发生了什么。
    • 我通常遵循 TS 路线图 (github.com/Microsoft/TypeScript/wiki/Roadmap) 来了解最新的新功能。我从这里阅读/提问中学到了很多东西。
    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2016-10-16
    相关资源
    最近更新 更多