如何将 R.pick 与 TypeScript 一起使用

Posted

技术标签:

【中文标题】如何将 R.pick 与 TypeScript 一起使用【英文标题】:How to use R.pick with TypeScript 【发布时间】:2020-04-16 08:49:08 【问题描述】:

我正在尝试类似的东西

import R from 'ramda'
import fs from 'fs'
import path from 'path'
import promisify from 'util'

const readFile = promisify(fs.readFile)

export async function discoverPackageInfo(): Promise<
  name: string,
  version: string
  description: string
> 
  return readFile(path.join(__dirname, '..', 'package.json'))
    .then(b => b.toString())
    .then(JSON.parse)
    .then(R.pick([
      'name',
      'description',
      'version',
    ]))

但我得到了

src/file.ts:13:3 - error TS2322: Type ' name: string; version: string; description: string;  | Pick<any, never>' is not assignable to type ' name: string; version: string; description: string; '.
  Type 'Pick<any, never>' is missing the following properties from type ' name: string; version: string; description: string; ': name, version, description

 13   return readFile(path.join(__dirname, '..', 'package.json'))
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 14     .then(b => b.toString())
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 19       'version',
    ~~~~~~~~~~~~~~~~
 20     ]))
    ~~~~~~~

我做错了什么?

【问题讨论】:

是的,我知道使用async.then 不太合乎情理。 async 在这里实际上是不必要的。没有它,代码也可以工作 正确。但是,我正在构建我的代码以在非 CPU 的任何东西上进行异步,以便强制正确地思考问题。最终成为结构和注释方面的练习,我发现它对我有很大帮助。 【参考方案1】:

JSON.parsereturns anyR.pick 之后的返回类型解析如下:

.then(R.pick([ 'name', 'description', 'version' ])) 
=>
type Return = Pick<any, Inner> // resolves to ; any stems from JSON.parse
type Inner = Exclude<keyof any,
    Exclude<keyof any, "name" | "description" | "version">> // never

你想要 name: string, version: string description: string ,但得到。为了解决这个问题,assert the desired type 显式为JSON.parse(我将其命名为MyThing):

readFile(path.join(__dirname, "..", "package.json"))
  .then(b => b.toString())
  .then(s => JSON.parse(s) as MyThing)
  .then(R.pick(["name", "description", "version"]));

Parsing can be made more type-safe,例如通过使用assertion functions / 类型守卫:

export async function discoverPackageInfo(): Promise<MyThing> 
  return readFile(...).then(...)
    .then(safeParse(assertMyThing)) // <-- change
    .then(R.pick(...));


const safeParse = <T>(assertFn: (o: any) => asserts o is T) => (s: string) => 
  const parsed = JSON.parse(s);
  assertFn(parsed);
  return parsed;


function assertMyThing(o: any): asserts o is MyThing 
  if (!("name" in o) || !("version" in o) || !("description" in o))
    throw Error();

Playground (playground 中的外部类型导入可能需要一些时间来加载,否则粘贴到您自己的环境中)

【讨论】:

帮了大忙,谢谢!有没有更标准/打包的方式来做到这一点?比如,从类型定义生成一个断言函数?我没有读过手册(我只是通过练习来学习),我会很感激任何指点! 我不确定断言函数的生成器。但是那里有很多自动生成的类型保护库,我个人觉得io-ts 和typescript-is 很有吸引力!并且似乎得到了积极的维护和支持。您也可以查看 these posts 作为起点。 顺便说一句:如果你想使用基于生成的类型保护的断言函数,转换应该没问题,比如this。 作为参考,我最终得到了这个:gist.github.com/pkoch/9062725ef441d80649f00964f9f424b7

以上是关于如何将 R.pick 与 TypeScript 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

如何将命名空间与 TypeScript 外部模块一起使用?

Angularjs + Typescript,如何将 $routeParams 与 IRouteParamsService 一起使用

将 Typescript 与 Jest 一起使用时如何测试超级调用?

如何将 Angular $http 与 typescript 泛型一起使用

如何将 TypeScript 与 withRouter、connect、React.Component 和自定义属性一起使用?

如何使用 Typescript 将 jest.spyOn 与 React 函数组件一起使用