Reputation: 2722
I was trying out something like
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',
]))
}
But I got
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 ]))
~~~~~~~
What am I doing wrong?
Upvotes: 3
Views: 1742
Reputation: 74500
JSON.parse
returns any
. The return type after R.pick
resolves like this:
.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
You want { name: string, version: string description: string }
, but get {}
. To fix that, assert the desired type explicitly for JSON.parse
(I named it 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, e.g. by using assertion functions / type guards:
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 (external type imports in the playground might take a bit to load, otherwise paste in your own environment)
Upvotes: 3