diff --git a/readme.md b/readme.md index 25c6ef5..305414f 100644 --- a/readme.md +++ b/readme.md @@ -17,7 +17,7 @@ npm i narrowing # yarn add narrowing let a: unknown; if (isXXX(a)) { - // TypeScritp know your type here! + // TypeScript know your type here! } ``` @@ -40,7 +40,7 @@ Basic: Advanced: -These functions help you make advanced type gurads. +These functions help you make advanced type guards. - [has](#has) - [kind](#kind) @@ -87,7 +87,7 @@ function testFunc(a: string, b: number): boolean { } if (isFunction(a)) { - a('11', 1); // no eror + a('11', 1); // no error } if (isInstance(a, Date)) { @@ -234,6 +234,60 @@ if (isSuccess(message)) { } ``` +schema supports a type argument for associating a schema with an existing type + +```ts +interface TestInterface { + id: number; + name: string; +} + +const isTestInterface = schema({ + id: isNumber, + name: isString +}); + +if (isTestInterface(message)) { + // let message: TestInterface + message; +} +``` + +```` + +### `every() + +Runtime array type validation. Checks each element of an array. + +```ts +let arr: unknown[] = [1, 2, 3]; +if (every(isNumber)(arr)) { + let typeCheck: number[] = arr; +} +```` + +Works with any narrowing validator, including schemas. + +```ts +interface TestInterface { + id: number; + name: string; +} + +const isTestInterface = schema({ + id: isNumber, + name: isString +}); + +let arr: unknown[] = [{ id: 1, name: 'aaa' }]; + +if (every(isTestInterface)(arr)) { + let typeCheck: TestInterface[] = arr; +} +``` + +``` + ## Version - 1.1.0 @@ -247,3 +301,4 @@ if (isSuccess(message)) { - 1.4.0 - replace ~~`isValidObject()`~~ with `schema()` - add `literal()` +``` diff --git a/src/index.ts b/src/index.ts index cb085de..e21d836 100644 --- a/src/index.ts +++ b/src/index.ts @@ -92,3 +92,12 @@ export function schema(schema: SchemaType): Predicate { return false; }; } + +export function every(predicate: Predicate): Predicate { + return function (value: unknown): value is T[] { + if (Array.isArray(value)) { + return value.every(predicate); + } + return false; + }; +} diff --git a/test/every.test.ts b/test/every.test.ts new file mode 100644 index 0000000..a3732b5 --- /dev/null +++ b/test/every.test.ts @@ -0,0 +1,82 @@ +import { schema, literal, every, isString, isNumber } from '../src/index'; + +test('fails when array contains elements of an unassignable type', () => { + expect(every(isString)([1, 2, 3])).toBe(false); + expect(every(isNumber)([1, '2', 3])).toBe(false); +}); + +test('passes when array contains correct type', () => { + expect(every(isNumber)([1, 2, 3])).toBe(true); + expect(every(isString)(['1', '2', '3'])).toBe(true); +}); + +test('passes when array is empty', () => { + expect(every(isString)([])).toBe(true); + expect(every(isNumber)([])).toBe(true); +}); + +interface TestInterface { + kind: 'test'; + value: { + nestedString: string; + nestedNumber: number; + }; +} + +const isTestInterface = schema({ + kind: literal('test'), + value: schema({ + nestedString: isString, + nestedNumber: isNumber + }) +}); + +test('passes when array contains valid objects', () => { + expect( + every(isTestInterface)([ + { + kind: 'test', + value: { + nestedString: 'string', + nestedNumber: 1 + } + } + ]) + ).toBe(true); +}); + +test('fails when array contains invalid objects', () => { + expect( + every(isTestInterface)([ + { + kind: 'test', + value: { + nested_string: 'string' + } + } + ]) + ).toBe(false); +}); + +every(isString); + +let arr: unknown[] = [1, 2, 3]; +if (every(isNumber)(arr)) { + let typeCheck: number[] = arr; +} + +interface TestInterfaceT { + id: number; + name: string; +} + +const isTestInterfaceT = schema({ + id: isNumber, + name: isString +}); + +let arr2: unknown[] = [{ id: 1, name: 'aaa' }]; + +if (every(isTestInterfaceT)(arr2)) { + let typeCheck: TestInterfaceT[] = arr2; +}