From 3890ce192c7a45b7ca7e7ce6a8e3ad47b603ad30 Mon Sep 17 00:00:00 2001 From: Andrej Dyck <43913051+andrej-dyck@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:46:15 +0200 Subject: [PATCH] use vitest typechecks --- package.json | 1 - pnpm-lock.yaml | 14 ++------- types/Branded.Age-Year.test.ts | 39 +++++++++++++++++++++++ types/Branded.Email.test.ts | 17 ++++++++++ types/Branded.test.ts | 57 ---------------------------------- 5 files changed, 59 insertions(+), 69 deletions(-) create mode 100644 types/Branded.Age-Year.test.ts create mode 100644 types/Branded.Email.test.ts delete mode 100644 types/Branded.test.ts diff --git a/package.json b/package.json index a987bf4..870a672 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ }, "devDependencies": { "@eslint/js": "9.7.0", - "@type-challenges/utils": "0.1.1", "@types/node": "20.14.11", "eslint": "9.7.0", "typescript": "5.5.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b366a68..4120f02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@eslint/js': specifier: 9.7.0 version: 9.7.0 - '@type-challenges/utils': - specifier: 0.1.1 - version: 0.1.1 '@types/node': specifier: 20.14.11 version: 20.14.11 @@ -308,9 +305,6 @@ packages: cpu: [x64] os: [win32] - '@type-challenges/utils@0.1.1': - resolution: {integrity: sha512-A7ljYfBM+FLw+NDyuYvGBJiCEV9c0lPWEAdzfOAkb3JFqfLl0Iv/WhWMMARHiRKlmmiD1g8gz/507yVvHdQUYA==} - '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -1174,8 +1168,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.9.0': optional: true - '@type-challenges/utils@0.1.1': {} - '@types/estree@1.0.5': {} '@types/node@20.14.11': @@ -1206,7 +1198,7 @@ snapshots: '@typescript-eslint/types': 7.16.1 '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) '@typescript-eslint/visitor-keys': 7.16.1 - debug: 4.3.4 + debug: 4.3.5 eslint: 9.7.0 optionalDependencies: typescript: 5.5.3 @@ -1222,7 +1214,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) '@typescript-eslint/utils': 7.16.1(eslint@9.7.0)(typescript@5.5.3) - debug: 4.3.4 + debug: 4.3.5 eslint: 9.7.0 ts-api-utils: 1.3.0(typescript@5.5.3) optionalDependencies: @@ -1236,7 +1228,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.16.1 '@typescript-eslint/visitor-keys': 7.16.1 - debug: 4.3.4 + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 diff --git a/types/Branded.Age-Year.test.ts b/types/Branded.Age-Year.test.ts new file mode 100644 index 0000000..e715f9a --- /dev/null +++ b/types/Branded.Age-Year.test.ts @@ -0,0 +1,39 @@ +import { expect, expectTypeOf, test } from 'vitest' +import { raise } from '../raise/index.js' +import { Branded } from './Branded.ts' + +/** branded numbers */ +type Age = Branded +type Year = Branded + +const validAge = (age: number): Age | undefined => + age >= 0 && age <= 125 ? age as Age : undefined + +const birthYear = (age: Age, now: Date): Year => + now.getFullYear() - age as Year // this is incorrect, but sufficient for demo purpose + +test('Age is compile-time save', () => { + const someAge = validAge(5) ?? raise('invalid age') + + expectTypeOf(someAge).toEqualTypeOf() + expectTypeOf(someAge).not.toEqualTypeOf() +}) + +test('birthYear is compile-time safe', () => { + const fiveYears = validAge(5) ?? raise('invalid age') + + expectTypeOf(birthYear).parameters.toMatchTypeOf<[Age, Date]>() + + const twothousandnineteen = birthYear(fiveYears, new Date(2024, 4, 11)) + expect(twothousandnineteen).toBe(2019) + + expectTypeOf(twothousandnineteen).toEqualTypeOf() + expectTypeOf(twothousandnineteen).not.toEqualTypeOf() +}) + +test('birthYear\'s argument is still just a number in JS', () => { + expect( + // @ts-expect-error argument must be Age + birthYear(5, new Date(2024, 4, 11)) + ).toBe(2019) +}) diff --git a/types/Branded.Email.test.ts b/types/Branded.Email.test.ts new file mode 100644 index 0000000..231675d --- /dev/null +++ b/types/Branded.Email.test.ts @@ -0,0 +1,17 @@ +import { expectTypeOf, test } from 'vitest' +import { raise } from '../raise/index.js' +import { Branded } from './Branded.js' + +/** branded string */ +type Email = Branded + +const emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ // a simplified regex +const validEmail = (email: string): Email | undefined => + emailRegex.test(email) ? email as Email : undefined + +test('Email is compile-time save', () => { + const someEmail = validEmail('abc@mail.com') ?? raise('invalid email') + + expectTypeOf(someEmail).toEqualTypeOf() + expectTypeOf(someEmail).not.toEqualTypeOf() +}) diff --git a/types/Branded.test.ts b/types/Branded.test.ts deleted file mode 100644 index 29e6fc0..0000000 --- a/types/Branded.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Equal, Expect, NotEqual } from '@type-challenges/utils' -import { expect, test } from 'vitest' -import { raise } from '../raise/index.js' -import { Branded } from './Branded.ts' - -/** branded numbers */ -type Age = Branded -type Year = Branded - -const validAge = (age: number): Age | undefined => - age >= 0 && age <= 125 ? age as Age : undefined - -const birthYear = (age: Age, now: Date): Year => - now.getFullYear() - age as Year // it's incorrect, but sufficient for demo purpose - -test('birthYear is compile-time safe', () => { - const fiveYears = validAge(5) ?? raise('invalid age') - expect( - birthYear(fiveYears, new Date(2024, 4, 11)) - ).toBe(2019) -}) - -test('birthYear\'s argument is still just a number in JS', () => { - expect( - // @ts-expect-error argument must be Age - birthYear(5, new Date(2024, 4, 11)) - ).toBe(2019) -}) - -const someAge = validAge(5) ?? raise('invalid age') -const someBirthYear = birthYear(someAge, new Date(2024, 4, 11)) - -// @ts-expect-error unused type as tests are compiler-based -type AgeTests = [ - Expect>, - Expect>, - Expect, [number]>>, - Expect, [Age, Date]>>, - Expect>, - Expect>, -] - -/** branded string */ -type Email = Branded - -const emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ // a simplified regex -const validEmail = (email: string): Email | undefined => - emailRegex.test(email) ? email as Email : undefined - -const someEmail = validEmail('abc@mail.com') ?? raise('invalid email') - -// @ts-expect-error unused type as tests are compiler-based -type EmailTests = [ - Expect>, - Expect>, - Expect, [string]>>, -]