From a48d2bd967122e1c29d6891fad0543e76c348a08 Mon Sep 17 00:00:00 2001 From: Simon Boudrias Date: Sun, 14 Jul 2024 12:43:52 -0400 Subject: [PATCH] Feat(@inquirer/type): Add new utility types to @inquirer/type + vitest type tests --- packages/prompts/prompts.test.mts | 21 ++++------- packages/type/src/index.mts | 58 ++----------------------------- packages/type/src/inquirer.mts | 23 ++++++++++++ packages/type/src/utils.mts | 25 +++++++++++++ packages/type/type.test.mts | 28 +++++++++++++++ 5 files changed, 85 insertions(+), 70 deletions(-) create mode 100644 packages/type/src/inquirer.mts create mode 100644 packages/type/src/utils.mts create mode 100644 packages/type/type.test.mts diff --git a/packages/prompts/prompts.test.mts b/packages/prompts/prompts.test.mts index 3c0c723ff..ab08e18ef 100644 --- a/packages/prompts/prompts.test.mts +++ b/packages/prompts/prompts.test.mts @@ -1,5 +1,4 @@ -import { describe, it, expect } from 'vitest'; -import { Expect, Equal } from '@inquirer/type'; +import { describe, it, expect, expectTypeOf } from 'vitest'; import { checkbox, confirm, @@ -24,16 +23,10 @@ describe('@inquirer/prompts', () => { expect(select).toBeTypeOf('function'); expect(Separator).toBeTypeOf('function'); }); -}); -/** - * Type assertions to validate the interfaces. - */ -export interface Test { - 1: Expect< - Equal< - NonNullable[0]['theme']>['helpMode'], - NonNullable[0]['theme']>['helpMode'] - > - >; -} + it('checkbox and select have matching helpMode', () => { + expectTypeOf< + NonNullable[0]['theme']>['helpMode'] + >().toEqualTypeOf[0]['theme']>['helpMode']>(); + }); +}); diff --git a/packages/type/src/index.mts b/packages/type/src/index.mts index 6efeaa6e3..746fd1ca2 100644 --- a/packages/type/src/index.mts +++ b/packages/type/src/index.mts @@ -1,56 +1,2 @@ -import * as readline from 'node:readline'; -import MuteStream from 'mute-stream'; - -export type InquirerReadline = readline.ReadLine & { - output: MuteStream; - input: NodeJS.ReadableStream; - clearLine: (dir: 0 | 1 | -1) => void; // https://nodejs.org/api/readline.html#rlclearlinedir -}; - -export class CancelablePromise extends Promise { - public cancel: () => void = () => {}; -} - -export type Prettify = { - [K in keyof T]: T[K]; - // eslint-disable-next-line @typescript-eslint/ban-types -} & {}; - -export type PartialDeep = T extends object - ? { - [P in keyof T]?: PartialDeep; - } - : T; - -export type Context = { - input?: NodeJS.ReadableStream; - output?: NodeJS.WritableStream; - clearPromptOnDone?: boolean; -}; - -export type Prompt = ( - config: Config, - context?: Context, -) => CancelablePromise; - -/** - * Utility types used for writing tests - * - * Equal checks that A and B are the same type, and returns - * either `true` or `false`. - * - * You can use it in combination with `Expect` to write type - * inference unit tests: - * - * ```ts - * type t = Expect< - * Equal, { a?: string }> - * > - * ``` - */ -export type Equal = - (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false; - -export type Expect = T; - -export type Not = T extends true ? false : true; +export * from './inquirer.mjs'; +export * from './utils.mjs'; diff --git a/packages/type/src/inquirer.mts b/packages/type/src/inquirer.mts new file mode 100644 index 000000000..8e8bb6084 --- /dev/null +++ b/packages/type/src/inquirer.mts @@ -0,0 +1,23 @@ +import * as readline from 'node:readline'; +import MuteStream from 'mute-stream'; + +export class CancelablePromise extends Promise { + public cancel: () => void = () => {}; +} + +export type InquirerReadline = readline.ReadLine & { + output: MuteStream; + input: NodeJS.ReadableStream; + clearLine: (dir: 0 | 1 | -1) => void; // https://nodejs.org/api/readline.html#rlclearlinedir +}; + +export type Context = { + input?: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + clearPromptOnDone?: boolean; +}; + +export type Prompt = ( + config: Config, + context?: Context, +) => CancelablePromise; diff --git a/packages/type/src/utils.mts b/packages/type/src/utils.mts new file mode 100644 index 000000000..1264da494 --- /dev/null +++ b/packages/type/src/utils.mts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/ban-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export type Prettify = { + [K in keyof T]: T[K]; +} & {}; + +export type PartialDeep = T extends object + ? { + [P in keyof T]?: PartialDeep; + } + : T; + +export type LiteralUnion = T | (F & {}); +export type KeyUnion = LiteralUnion>; + +export type DistributiveMerge = A extends any + ? Prettify & B> + : never; + +export type UnionToIntersection = ( + T extends any ? (input: T) => void : never +) extends (input: infer Intersection) => void + ? Intersection + : never; diff --git a/packages/type/type.test.mts b/packages/type/type.test.mts new file mode 100644 index 000000000..a67a455dc --- /dev/null +++ b/packages/type/type.test.mts @@ -0,0 +1,28 @@ +import { test, expectTypeOf } from 'vitest'; +import type { + DistributiveMerge, + PartialDeep, + UnionToIntersection, +} from './src/index.mjs'; + +test('DistributiveMerge', () => { + expectTypeOf< + DistributiveMerge<{ a: string; b: string }, { b: number }> + >().toEqualTypeOf<{ + a: string; + b: number; + }>(); +}); + +test('PartialDeep', () => { + expectTypeOf>().toEqualTypeOf<{ a?: string }>(); + expectTypeOf>().toEqualTypeOf<{ + a?: { b?: string }; + }>(); +}); + +test('UnionToIntersection', () => { + expectTypeOf>().toEqualTypeOf< + { x: string } & { y: number } + >(); +});