如何在 TypeScript 中使用 R.pick

How to use R.pick with TypeScript

我正在尝试类似

的东西
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     ]))
    ~~~~~~~

我做错了什么?

JSON.parse returns any. The return type after R.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"]));

, e.g. by using 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 中的外部类型导入可能需要一点加载,否则粘贴到您自己的环境中)