From f02e9ae4ddf6d14ea79f8aea32c7182dce82cb3b Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Tue, 12 Jun 2018 13:57:32 +0200 Subject: [PATCH 1/4] [webpack-config-utils]: add type definitions --- types/webpack-config-utils/index.d.ts | 64 +++++++++++ types/webpack-config-utils/tsconfig.json | 23 ++++ types/webpack-config-utils/tslint.json | 1 + .../webpack-config-utils-tests.ts | 101 ++++++++++++++++++ 4 files changed, 189 insertions(+) create mode 100644 types/webpack-config-utils/index.d.ts create mode 100644 types/webpack-config-utils/tsconfig.json create mode 100644 types/webpack-config-utils/tslint.json create mode 100644 types/webpack-config-utils/webpack-config-utils-tests.ts diff --git a/types/webpack-config-utils/index.d.ts b/types/webpack-config-utils/index.d.ts new file mode 100644 index 0000000000..829f88296e --- /dev/null +++ b/types/webpack-config-utils/index.d.ts @@ -0,0 +1,64 @@ +// Type definitions for webpack-config-utils 2.3 +// Project: https://github.com/kentcdodds/webpack-config-utils#readme +// Definitions by: Martin Hochel +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +/*~ You can declare properties of the module using const, let, or var */ +// declare const getIfUtils: WebpackConfigUtils.GetIfUtils; +// declare const removeEmpty: WebpackConfigUtils.RemoveEmpty; +// declare const propIf: WebpackConfigUtils.PropIf; +// declare const propIfNot: WebpackConfigUtils.PropIfNot; + +declare const api: WebpackConfigUtils.API; +export = api; + +declare namespace WebpackConfigUtils { + type Falsy = false | '' | undefined | null | 0; + type DefinedObjKeys = ({ [P in keyof T]: T[P] extends undefined ? never : P })[keyof T]; + type NonEmptyObject = DefinedObjKeys> = { [PP in P]: T[PP] }; + + type EnvVars = 'production' | 'prod' | 'test' | 'development' | 'dev'; + interface RemoveEmpty { + (input: Array): T[]; + (input: { [P in keyof T]: T[P] }): NonEmptyObject; + } + type GetIfUtils = ( + env: { [P in E]: boolean | string } | E, + vars?: Array + ) => IfUtils; + // @TODO + // with following defintion, generics will get flattened to base type -> string. Any ideas why or how to fix this? + // $ExpectType "value" | "alternate" + // propIf(true, 'value', 'alternate'); // 'value' + // + // type PropIf = (add: A, value: I, alternate: E) => A extends Falsy ? E : I; + // type PropIfNot = (add: A, value: I, alternate: E) => A extends Falsy ? I : E; + type PropIf = (add: any, value: I, alternate: E) => I | E; + type PropIfNot = PropIf; + + interface IfUtilsFn { + (value: Y, alternate?: N): Y | N; + (): boolean; + } + interface IfUtils { + ifDevelopment: IfUtilsFn; + ifNotDevelopment: IfUtilsFn; + ifDev: IfUtilsFn; + ifNotDev: IfUtilsFn; + ifProduction: IfUtilsFn; + ifNotProduction: IfUtilsFn; + ifProd: IfUtilsFn; + ifNotProd: IfUtilsFn; + ifTest: IfUtilsFn; + ifNotTest: IfUtilsFn; + [key: string]: IfUtilsFn; + } + + interface API { + getIfUtils: GetIfUtils; + removeEmpty: RemoveEmpty; + propIf: PropIf; + propIfNot: PropIfNot; + } +} diff --git a/types/webpack-config-utils/tsconfig.json b/types/webpack-config-utils/tsconfig.json new file mode 100644 index 0000000000..0376cbd868 --- /dev/null +++ b/types/webpack-config-utils/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "webpack-config-utils-tests.ts" + ] +} diff --git a/types/webpack-config-utils/tslint.json b/types/webpack-config-utils/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/webpack-config-utils/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" } diff --git a/types/webpack-config-utils/webpack-config-utils-tests.ts b/types/webpack-config-utils/webpack-config-utils-tests.ts new file mode 100644 index 0000000000..a8ca450b20 --- /dev/null +++ b/types/webpack-config-utils/webpack-config-utils-tests.ts @@ -0,0 +1,101 @@ +import webpackConfigUtils = require('webpack-config-utils'); +import { getIfUtils, removeEmpty, propIf, propIfNot } from 'webpack-config-utils'; + +{ + // propIf/propIfNot + + // $ExpectType "value" | "alternate" + propIf(true, 'value', 'alternate'); // 'value' + + // $ExpectType "value" | "alternate" + propIf(false, 'value', 'alternate'); // 'alternate' + + // $ExpectType "value" | "alternate" + propIf('false', 'value', 'alternate'); // 'alternate' + + // $ExpectType "value" | "alternate" + propIfNot(true, 'value', 'alternate'); // 'alternate' + + // $ExpectType "value" | "alternate" + propIfNot(false, 'value', 'alternate'); // 'value' + + // $ExpectType "value" | "alternate" + propIfNot('false', 'value', 'alternate'); // 'value' +} +{ + // getIfUtils + { + const expectedMethods = [ + 'ifProduction', + 'ifNotProduction', + 'ifProd', + 'ifNotProd', + 'ifTest', + 'ifNotTest', + 'ifDevelopment', + 'ifNotDevelopment', + 'ifDev', + 'ifNotDev' + ]; + const utils = getIfUtils({}); + } + { + const expectedMethods = ['ifFoo', 'ifNotFoo', 'ifBar', 'ifNotBar']; + const utils = getIfUtils({}, ['foo', 'bar']); + const { ifFoo, ifBar, ifNotFoo, ifNotBar } = utils; + } + + { + const { ifProduction } = getIfUtils('production'); + + // $ExpectType "value" | "alternate" + ifProduction('value', 'alternate'); // 'value' + } + { + const { ifNotDev } = getIfUtils({ dev: false }); + + // $ExpectType "value" | "alternate" + ifNotDev('value', 'alternate'); // 'value' + } + { + // $ExpectError + getIfUtils(false); // webpack-config-utils:getIfUtils.*?string\/Object/); + } + { + const { ifTest, ifProd, ifNotDev } = getIfUtils('test'); + // $ExpectType boolean + ifTest(); // true; + // $ExpectType boolean + ifProd(); // false; + // $ExpectType boolean + ifNotDev(); // true; + } + { + const { ifWatch, ifProd, ifNotDev, ifTest } = getIfUtils('watch', ['prod', 'dev', 'watch']); + // $ExpectType boolean + ifWatch(); // true + // $ExpectType boolean + ifProd(); // false + // $ExpectType boolean + ifNotDev(); // true + // $ExpectType IfUtilsFn + ifTest; // function + } +} +{ + // removeEmpty + // $ExpectType (number | null)[] + const emptiedArray = removeEmpty([undefined, 0, 1, 2, undefined, 3, undefined, null]); // [0, 1, 2, 3, null] + + // $ExpectType NonEmptyObject<{ a: number; b: string; c: undefined; d: null; }, "b" | "a" | "d"> + const emptiedObject = removeEmpty({ a: 1, b: 'b', c: undefined, d: null }); // {a: 1, b: 'b', d: null} + const {a, b, d} = emptiedObject; + + { + // $ExpectError + const {a, b, c, d} = emptiedObject; // Error + } + + // $ExpectType number | null + const firstItem = emptiedArray[0]; +} From 6e5cd007757e175ce116fd82f5aaa41ee9f64a6a Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Tue, 12 Jun 2018 14:18:41 +0200 Subject: [PATCH 2/4] [webpack-config-utils]: remove unnecessary comments --- types/webpack-config-utils/index.d.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/types/webpack-config-utils/index.d.ts b/types/webpack-config-utils/index.d.ts index 829f88296e..4f2f70a409 100644 --- a/types/webpack-config-utils/index.d.ts +++ b/types/webpack-config-utils/index.d.ts @@ -4,12 +4,6 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 -/*~ You can declare properties of the module using const, let, or var */ -// declare const getIfUtils: WebpackConfigUtils.GetIfUtils; -// declare const removeEmpty: WebpackConfigUtils.RemoveEmpty; -// declare const propIf: WebpackConfigUtils.PropIf; -// declare const propIfNot: WebpackConfigUtils.PropIfNot; - declare const api: WebpackConfigUtils.API; export = api; @@ -27,6 +21,7 @@ declare namespace WebpackConfigUtils { env: { [P in E]: boolean | string } | E, vars?: Array ) => IfUtils; + // @TODO // with following defintion, generics will get flattened to base type -> string. Any ideas why or how to fix this? // $ExpectType "value" | "alternate" @@ -34,6 +29,7 @@ declare namespace WebpackConfigUtils { // // type PropIf = (add: A, value: I, alternate: E) => A extends Falsy ? E : I; // type PropIfNot = (add: A, value: I, alternate: E) => A extends Falsy ? I : E; + type PropIf = (add: any, value: I, alternate: E) => I | E; type PropIfNot = PropIf; From 0aacc294d9f79272860166ef3f44cbdaec8f20ce Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Tue, 12 Jun 2018 16:22:27 +0200 Subject: [PATCH 3/4] [webpack-config-utils]: add more dtslint assertions --- .../webpack-config-utils-tests.ts | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/types/webpack-config-utils/webpack-config-utils-tests.ts b/types/webpack-config-utils/webpack-config-utils-tests.ts index a8ca450b20..830997eb7e 100644 --- a/types/webpack-config-utils/webpack-config-utils-tests.ts +++ b/types/webpack-config-utils/webpack-config-utils-tests.ts @@ -25,28 +25,23 @@ import { getIfUtils, removeEmpty, propIf, propIfNot } from 'webpack-config-utils { // getIfUtils { - const expectedMethods = [ - 'ifProduction', - 'ifNotProduction', - 'ifProd', - 'ifNotProd', - 'ifTest', - 'ifNotTest', - 'ifDevelopment', - 'ifNotDevelopment', - 'ifDev', - 'ifNotDev' - ]; + // $ExpectType IfUtils const utils = getIfUtils({}); } { - const expectedMethods = ['ifFoo', 'ifNotFoo', 'ifBar', 'ifNotBar']; - const utils = getIfUtils({}, ['foo', 'bar']); - const { ifFoo, ifBar, ifNotFoo, ifNotBar } = utils; + const utils = getIfUtils({}, ['foo', 'bar']); // 'ifFoo', 'ifNotFoo', 'ifBar', 'ifNotBar' + const { + ifFoo, // $ExpectType IfUtilsFn + ifBar, // $ExpectType IfUtilsFn + ifNotFoo, // $ExpectType IfUtilsFn + ifNotBar // $ExpectType IfUtilsFn + } = utils; } { - const { ifProduction } = getIfUtils('production'); + const { + ifProduction // $ExpectType IfUtilsFn + } = getIfUtils('production'); // $ExpectType "value" | "alternate" ifProduction('value', 'alternate'); // 'value' @@ -89,11 +84,15 @@ import { getIfUtils, removeEmpty, propIf, propIfNot } from 'webpack-config-utils // $ExpectType NonEmptyObject<{ a: number; b: string; c: undefined; d: null; }, "b" | "a" | "d"> const emptiedObject = removeEmpty({ a: 1, b: 'b', c: undefined, d: null }); // {a: 1, b: 'b', d: null} - const {a, b, d} = emptiedObject; + const { + a, // $ExpectType number + b, // $ExpectType string + d // $ExpectType null + } = emptiedObject; { // $ExpectError - const {a, b, c, d} = emptiedObject; // Error + const {a, b, c, d} = emptiedObject; } // $ExpectType number | null From f29026805f8db3ada99bd512440fea629aadfbd8 Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Fri, 22 Jun 2018 18:37:11 +0200 Subject: [PATCH 4/4] [webpack-config-utils]: add proper overrides to resolve if/else type properly --- types/webpack-config-utils/index.d.ts | 80 +++++++------------ .../webpack-config-utils-tests.ts | 24 +++--- 2 files changed, 42 insertions(+), 62 deletions(-) diff --git a/types/webpack-config-utils/index.d.ts b/types/webpack-config-utils/index.d.ts index 4f2f70a409..97c1fdf397 100644 --- a/types/webpack-config-utils/index.d.ts +++ b/types/webpack-config-utils/index.d.ts @@ -4,57 +4,37 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 -declare const api: WebpackConfigUtils.API; -export = api; +export function getIfUtils( + env: { [P in E]: boolean | string } | E, + vars?: Array +): IfUtils; +export function removeEmpty(input: Array): T[]; +export function removeEmpty(input: { [P in keyof T]: T[P] }): NonEmptyObject; +export function propIf(a: Falsy, value: any, alternate: E): E; +export function propIf(a: any, value: I, alternate: any): I; +export function propIfNot(a: Falsy, value: I, alternate: any): I; +export function propIfNot(a: any, value: any, alternate: E): E; -declare namespace WebpackConfigUtils { - type Falsy = false | '' | undefined | null | 0; - type DefinedObjKeys = ({ [P in keyof T]: T[P] extends undefined ? never : P })[keyof T]; - type NonEmptyObject = DefinedObjKeys> = { [PP in P]: T[PP] }; +export type Falsy = false | '' | 'false' | undefined | null | 0; +export type DefinedObjKeys = ({ [P in keyof T]: T[P] extends undefined ? never : P })[keyof T]; +export type NonEmptyObject = DefinedObjKeys> = { [PP in P]: T[PP] }; - type EnvVars = 'production' | 'prod' | 'test' | 'development' | 'dev'; - interface RemoveEmpty { - (input: Array): T[]; - (input: { [P in keyof T]: T[P] }): NonEmptyObject; - } - type GetIfUtils = ( - env: { [P in E]: boolean | string } | E, - vars?: Array - ) => IfUtils; +export type EnvVars = 'production' | 'prod' | 'test' | 'development' | 'dev'; - // @TODO - // with following defintion, generics will get flattened to base type -> string. Any ideas why or how to fix this? - // $ExpectType "value" | "alternate" - // propIf(true, 'value', 'alternate'); // 'value' - // - // type PropIf = (add: A, value: I, alternate: E) => A extends Falsy ? E : I; - // type PropIfNot = (add: A, value: I, alternate: E) => A extends Falsy ? I : E; - - type PropIf = (add: any, value: I, alternate: E) => I | E; - type PropIfNot = PropIf; - - interface IfUtilsFn { - (value: Y, alternate?: N): Y | N; - (): boolean; - } - interface IfUtils { - ifDevelopment: IfUtilsFn; - ifNotDevelopment: IfUtilsFn; - ifDev: IfUtilsFn; - ifNotDev: IfUtilsFn; - ifProduction: IfUtilsFn; - ifNotProduction: IfUtilsFn; - ifProd: IfUtilsFn; - ifNotProd: IfUtilsFn; - ifTest: IfUtilsFn; - ifNotTest: IfUtilsFn; - [key: string]: IfUtilsFn; - } - - interface API { - getIfUtils: GetIfUtils; - removeEmpty: RemoveEmpty; - propIf: PropIf; - propIfNot: PropIfNot; - } +export interface IfUtilsFn { + (value: Y, alternate?: N): Y | N; + (): boolean; +} +export interface IfUtils { + ifDevelopment: IfUtilsFn; + ifNotDevelopment: IfUtilsFn; + ifDev: IfUtilsFn; + ifNotDev: IfUtilsFn; + ifProduction: IfUtilsFn; + ifNotProduction: IfUtilsFn; + ifProd: IfUtilsFn; + ifNotProd: IfUtilsFn; + ifTest: IfUtilsFn; + ifNotTest: IfUtilsFn; + [key: string]: IfUtilsFn; } diff --git a/types/webpack-config-utils/webpack-config-utils-tests.ts b/types/webpack-config-utils/webpack-config-utils-tests.ts index 830997eb7e..4f83245b57 100644 --- a/types/webpack-config-utils/webpack-config-utils-tests.ts +++ b/types/webpack-config-utils/webpack-config-utils-tests.ts @@ -4,23 +4,23 @@ import { getIfUtils, removeEmpty, propIf, propIfNot } from 'webpack-config-utils { // propIf/propIfNot - // $ExpectType "value" | "alternate" - propIf(true, 'value', 'alternate'); // 'value' + // $ExpectType "value" + propIf(true, 'value', 'alternate'); - // $ExpectType "value" | "alternate" - propIf(false, 'value', 'alternate'); // 'alternate' + // $ExpectType "alternate" + propIf(false, 'value', 'alternate'); - // $ExpectType "value" | "alternate" - propIf('false', 'value', 'alternate'); // 'alternate' + // $ExpectType "alternate" + propIf('false', 'value', 'alternate'); - // $ExpectType "value" | "alternate" - propIfNot(true, 'value', 'alternate'); // 'alternate' + // $ExpectType "alternate" + propIfNot(true, 'value', 'alternate'); - // $ExpectType "value" | "alternate" - propIfNot(false, 'value', 'alternate'); // 'value' + // $ExpectType "value" + propIfNot(false, 'value', 'alternate'); - // $ExpectType "value" | "alternate" - propIfNot('false', 'value', 'alternate'); // 'value' + // $ExpectType "value" + propIfNot('false', 'value', 'alternate'); } { // getIfUtils