From f3b13e30f81866c92bf3438f749ca1f0b9472b5a Mon Sep 17 00:00:00 2001 From: skarab42 Date: Wed, 21 Sep 2022 08:58:58 +0200 Subject: [PATCH] Fix `PackageJson` and `JsonObject` types (#465) --- source/basic.d.ts | 2 +- source/package-json.d.ts | 8 +++++--- test-d/package-json.ts | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/source/basic.d.ts b/source/basic.d.ts index ac74fdc9a..1c637e7a0 100644 --- a/source/basic.d.ts +++ b/source/basic.d.ts @@ -19,7 +19,7 @@ This type can be useful to enforce some input to be JSON-compatible or as a supe @category JSON */ -export type JsonObject = {[Key in string]?: JsonValue}; +export type JsonObject = {[Key in string]: JsonValue} & {[Key in string]?: JsonValue | undefined}; /** Matches a JSON array. diff --git a/source/package-json.d.ts b/source/package-json.d.ts index f511699d4..513e6b11b 100644 --- a/source/package-json.d.ts +++ b/source/package-json.d.ts @@ -1,4 +1,5 @@ import type {LiteralUnion} from './literal-union'; +import type {JsonObject, JsonValue} from './basic'; declare namespace PackageJson { /** @@ -27,7 +28,7 @@ declare namespace PackageJson { }; export type DirectoryLocations = { - [directoryType: string]: unknown; + [directoryType: string]: JsonValue | undefined; /** Location for executable scripts. Sugar to generate entries in the `bin` property by walking the folder. @@ -483,7 +484,7 @@ declare namespace PackageJson { /** Is used to set configuration parameters used in package scripts that persist across upgrades. */ - config?: Record; + config?: JsonObject; /** The dependencies of the package. @@ -648,7 +649,7 @@ declare namespace PackageJson { /** Additional, less common properties from the [npm docs on `publishConfig`](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#publishconfig). */ - [additionalProperties: string]: unknown; + [additionalProperties: string]: JsonValue | undefined; /** When publishing scoped packages, the access level defaults to restricted. If you want your scoped package to be publicly viewable (and installable) set `--access=public`. The only valid values for access are public and restricted. Unscoped packages always have an access level of public. @@ -677,6 +678,7 @@ Type for [npm's `package.json` file](https://docs.npmjs.com/creating-a-package-j @category File */ export type PackageJson = +JsonObject & PackageJson.NodeJsStandard & PackageJson.PackageJsonStandard & PackageJson.NonStandardEntryPoints & diff --git a/test-d/package-json.ts b/test-d/package-json.ts index e43a46b4e..23812e779 100644 --- a/test-d/package-json.ts +++ b/test-d/package-json.ts @@ -1,5 +1,5 @@ -import {expectType, expectAssignable, expectNotAssignable} from 'tsd'; -import type {PackageJson, LiteralUnion} from '../index'; +import {expectType, expectAssignable, expectNotAssignable, expectError} from 'tsd'; +import type {PackageJson, LiteralUnion, JsonObject} from '../index'; const packageJson: PackageJson = {}; @@ -26,7 +26,7 @@ expectType<{type: string; url: string; directory?: string} | string | undefined> packageJson.repository, ); expectType(packageJson.scripts); -expectType | undefined>(packageJson.config); +expectType(packageJson.config); expectType(packageJson.dependencies); expectType(packageJson.devDependencies); expectType( @@ -81,4 +81,30 @@ expectAssignable({ '<4': undefined, }); -expectNotAssignable>(packageJson); +// Must reject an object that contains properties with `undefined` values. +// See https://github.com/sindresorhus/type-fest/issues/272 +declare function setConfig(config: JsonObject): void; + +expectError(setConfig({bugs: undefined})); +expectError(setConfig({bugs: {life: undefined}})); + +expectNotAssignable({bugs: undefined}); +expectNotAssignable({bugs: {life: undefined}}); + +expectAssignable({}); +expectAssignable({bugs: 42}); +expectAssignable({bugs: [42]}); +expectAssignable({bugs: {life: 42}}); + +// `PackageJson` should be a valid `JsonObject`. +// See https://github.com/sindresorhus/type-fest/issues/79 +type UnknownRecord = Record; + +const unknownRecord: UnknownRecord = {}; +const jsonObject: JsonObject = {}; + +expectAssignable(packageJson); +expectNotAssignable(unknownRecord); + +expectAssignable(jsonObject); +expectAssignable(packageJson);