diff --git a/.github/workflows/build-status.yml b/.github/workflows/build-status.yml index dc3c4ea8..02ad0dc9 100644 --- a/.github/workflows/build-status.yml +++ b/.github/workflows/build-status.yml @@ -506,15 +506,6 @@ jobs: else pnpm --filter {./packages/**} --parallel -c exec "cd test-types && pnpm --package typescript@${{ matrix.ts-version }} dlx tsc --noEmit --skipLibCheck --strict --module NodeNext --moduleResolution NodeNext *.mts" fi - - name: Check in ES Modules mode with exactOptionalPropertyTypes - run: | - if [ "${{ matrix.ts-version }}" = "5.0" ] || [ "${{ matrix.ts-version }}" = "5.7" ]; then - pnpm --filter {./packages/**} --filter '!@fast-check/ava' --parallel -c exec "sed -i 's/fc-expect-error-require-exactOptionalPropertyTypes/ts-expect-error/g' test-types/*.mts" - pnpm --filter {./packages/**} --filter '!@fast-check/ava' --parallel -c exec "cd test-types && pnpm --package typescript@${{ matrix.ts-version }} dlx tsc --noEmit --skipLibCheck --strict --exactOptionalPropertyTypes *.mts" - else - pnpm --filter {./packages/**} --parallel -c exec "sed -i 's/fc-expect-error-require-exactOptionalPropertyTypes/ts-expect-error/g' test-types/*.mts" - pnpm --filter {./packages/**} --parallel -c exec "cd test-types && pnpm --package typescript@${{ matrix.ts-version }} dlx tsc --noEmit --skipLibCheck --strict --exactOptionalPropertyTypes *.mts" - fi # Job to confirm every required job passed pre_all_checks_passed: diff --git a/.github/zizmor.yml b/.github/zizmor.yml index 495f0464..1a0b287d 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -9,23 +9,23 @@ rules: ignore: # These are false positives - attestations ensure artifact integrity # setup-node actions in publish jobs followed by gh-release for attestation uploads - - build-status.yml:644 # fast-check - - build-status.yml:718 # ava - - build-status.yml:792 # jest - - build-status.yml:866 # packaged - - build-status.yml:940 # poisoning - - build-status.yml:1014 # vitest - - build-status.yml:1088 # worker - - build-status.yml:1162 # expect-type + - build-status.yml:635 # fast-check + - build-status.yml:709 # ava + - build-status.yml:783 # jest + - build-status.yml:857 # packaged + - build-status.yml:931 # poisoning + - build-status.yml:1005 # vitest + - build-status.yml:1079 # worker + - build-status.yml:1153 # expect-type template-injection: ignore: # These are safe - TGZ_NAME is set from package.json version in controlled environment # bundle-path is output from attest-build-provenance action (trusted GitHub action) - - build-status.yml:676 # fast-check - - build-status.yml:750 # ava - - build-status.yml:824 # jest - - build-status.yml:898 # packaged - - build-status.yml:972 # poisoning - - build-status.yml:1046 # vitest - - build-status.yml:1120 # worker - - build-status.yml:1194 # expect-type + - build-status.yml:667 # fast-check + - build-status.yml:741 # ava + - build-status.yml:815 # jest + - build-status.yml:889 # packaged + - build-status.yml:963 # poisoning + - build-status.yml:1037 # vitest + - build-status.yml:1111 # worker + - build-status.yml:1185 # expect-type diff --git a/packages/fast-check/package.json b/packages/fast-check/package.json index b4040861..606f23c1 100644 --- a/packages/fast-check/package.json +++ b/packages/fast-check/package.json @@ -65,7 +65,6 @@ "pure-rand": "^7.0.0" }, "devDependencies": { - "@fast-check/expect-type": "workspace:*", "@fast-check/poisoning": "workspace:*", "@microsoft/api-extractor": "^7.55.2", "@types/node": "^24.10.7", diff --git a/packages/fast-check/test-types/main.ts b/packages/fast-check/test-types/main.ts index 8b041447..fc1a92a5 100644 --- a/packages/fast-check/test-types/main.ts +++ b/packages/fast-check/test-types/main.ts @@ -3,14 +3,13 @@ // and unknown syntaxes at build time import fc from 'fast-check'; -import { expectType, expectTypeAssignable } from '@fast-check/expect-type'; +import { expectTypeOf } from 'vitest'; // assert -expectType()(fc.assert(fc.property(fc.nat(), () => {})), 'Synchronous property means synchronous assert'); -expectType>()( - fc.assert(fc.asyncProperty(fc.nat(), async () => {})), - 'Asynchronous property means asynchronous assert', -); +// Synchronous property means synchronous assert +expectTypeOf(fc.assert(fc.property(fc.nat(), () => {}))).toEqualTypeOf(); +// Asynchronous property means asynchronous assert +expectTypeOf(fc.assert(fc.asyncProperty(fc.nat(), async () => {}))).toEqualTypeOf>(); // assert (beforeEach, afterEach) // @ts-expect-error - Synchronous properties do not accept asynchronous beforeEach @@ -19,13 +18,13 @@ fc.assert(fc.property(fc.nat(), () => {}).beforeEach(async () => {})); fc.assert(fc.property(fc.nat(), () => {}).afterEach(async () => {})); // assert (reporter) -expectType()( +// Accept a reporter featuring the right types +expectTypeOf( fc.assert( fc.property(fc.nat(), fc.string(), (_a, _b) => {}), { reporter: (_out: fc.RunDetails<[number, string]>) => {} }, ), - 'Accept a reporter featuring the right types', -); +).toEqualTypeOf(); // prettier-ignore // @ts-expect-error - Reporter must be compatible with generated values fc.assert(fc.property(fc.nat(), () => {}), { reporter: (_out: fc.RunDetails<[string, string]>) => {} }); @@ -33,128 +32,113 @@ fc.assert(fc.property(fc.nat(), () => {}), { reporter: (_out: fc.RunDetails<[str fc.property(fc.nat(), fc.string(), async (_a: number) => {}); // property -expectTypeAssignable>()( - fc.property(fc.nat(), (_a) => {}), - '"property" instanciates instances compatible with IProperty', -); -expectTypeAssignable>()( - fc.property(fc.nat(), fc.string(), (_a, _b) => {}), - '"property" handles tuples', -); -expectType()( +// "property" instantiates instances compatible with IProperty +expectTypeOf(fc.property(fc.nat(), (_a) => {})).toMatchTypeOf>(); +// "property" handles tuples +expectTypeOf(fc.property(fc.nat(), fc.string(), (_a, _b) => {})).toMatchTypeOf>(); +// Synchronous property accepts synchronous hooks +expectTypeOf( fc.assert( fc .property(fc.nat(), () => {}) .beforeEach(() => 123) .afterEach(() => 'anything'), ), - 'Synchronous property accepts synchronous hooks', -); +).toEqualTypeOf(); // @ts-expect-error - Types declared in predicate are not compatible with the generators fc.property(fc.nat(), fc.string(), (_a: number, _b: number) => {}); // asyncProperty -expectTypeAssignable>()( - fc.asyncProperty(fc.nat(), async (_a) => {}), - '"asyncProperty" instanciates instances compatible with IAsyncProperty', -); -expectTypeAssignable>()( - fc.asyncProperty(fc.nat(), fc.string(), async (_a, _b) => {}), - '"asyncProperty" handles tuples', -); -expectTypeAssignable>()( +// "asyncProperty" instantiates instances compatible with IAsyncProperty +expectTypeOf(fc.asyncProperty(fc.nat(), async (_a) => {})).toMatchTypeOf>(); +// "asyncProperty" handles tuples +expectTypeOf(fc.asyncProperty(fc.nat(), fc.string(), async (_a, _b) => {})).toMatchTypeOf< + fc.IAsyncProperty<[number, string]> +>(); +// Asynchronous property accepts asynchronous hooks +expectTypeOf( fc .asyncProperty(fc.nat(), async (_a) => {}) .beforeEach(async () => 123) .afterEach(async () => 'anything'), - 'Asynchronous property accepts asynchronous hooks', -); -expectTypeAssignable>()( +).toMatchTypeOf>(); +// Asynchronous property accepts synchronous hooks +expectTypeOf( fc .asyncProperty(fc.nat(), async (_a) => {}) .beforeEach(() => 123) .afterEach(() => 'anything'), - 'Asynchronous property accepts synchronous hooks', -); +).toMatchTypeOf>(); // @ts-expect-error - Types declared in predicate are not compatible with the generators fc.asyncProperty(fc.nat(), fc.string(), async (_a: number, _b: number) => {}); // @ts-expect-error - Enforce users to declare all the generated values as arguments of the predicate fc.asyncProperty(fc.nat(), fc.string(), async (_a: number) => {}); // base arbitrary (chain) -expectType>()( - fc.nat().chain((n) => fc.array(fc.string(), { maxLength: n })), - 'Type of "chain" corresponds to the return type of the passed lambda', -); -expectType>()( - fc.constantFrom(1, 2, 3).chain((value) => fc.constant(value)), - 'Type of "chain" corresponds to the return type of the passed lambda', -); -expectType>()( - // without the as, TypeScript refused to compile as constantFrom requires t least one argument +// Type of "chain" corresponds to the return type of the passed lambda +expectTypeOf(fc.nat().chain((n) => fc.array(fc.string(), { maxLength: n }))).toEqualTypeOf>(); +// Type of "chain" corresponds to the return type of the passed lambda +expectTypeOf(fc.constantFrom(1, 2, 3).chain((value) => fc.constant(value))).toEqualTypeOf>(); +// Type of "chain" should not simplify the type to something more general (no "1 -> number" expected) +expectTypeOf( + // without the as, TypeScript refused to compile as constantFrom requires at least one argument fc.constantFrom(...([1, 2, 3] as [1, 2, 3])).chain((value) => fc.constant(value)), - 'Type of "chain" should not simplify the type to something more general (no "1 -> number" expected)', -); +).toEqualTypeOf>(); // base arbitrary (filter) -expectType>()( - fc.option(fc.nat()).filter((n): n is number => n !== null), - '"filter" preserves the source type', -); +// "filter" preserves the source type +expectTypeOf(fc.option(fc.nat()).filter((n): n is number => n !== null)).toEqualTypeOf>(); // base arbitrary (map) -expectType>()( - fc.nat().map((n) => String(n)), - '"map" alters the resulting type', -); +// "map" alters the resulting type +expectTypeOf(fc.nat().map((n) => String(n))).toEqualTypeOf>(); // constant arbitrary -expectType>()(fc.constant(1), 'By default, "constant" preserves the precise type'); -expectType>()(fc.constant(1), 'But it also accepts to receive the type'); +// By default, "constant" preserves the precise type +expectTypeOf(fc.constant(1)).toEqualTypeOf>(); +// But it also accepts to receive the type +expectTypeOf(fc.constant(1)).toEqualTypeOf>(); // constantFrom arbitrary -expectType>()(fc.constantFrom(1, 2), 'By default, "constantFrom" preserves the precise type'); -expectType>()(fc.constantFrom(1, 2), 'But it also accepts to receive the type'); -expectType>()( - fc.constantFrom(...([1, 2] as const)), - '"as const" was a way to prevent extra simplification of "constantFrom", it\'s now not needed anymore', -); -expectType>()( - fc.constantFrom(1, 2, 'hello'), - '"constantFrom" accepts arguments not having the same types without any typing trick', -); -expectType>()( - fc.constantFrom(...([1, 2, 'hello'] as const)), - '"as const" was a way to prevent extra simplification of "constantFrom"', -); +// By default, "constantFrom" preserves the precise type +expectTypeOf(fc.constantFrom(1, 2)).toEqualTypeOf>(); +// But it also accepts to receive the type +expectTypeOf(fc.constantFrom(1, 2)).toEqualTypeOf>(); +// "as const" was a way to prevent extra simplification of "constantFrom", it's now not needed anymore +expectTypeOf(fc.constantFrom(...([1, 2] as const))).toEqualTypeOf>(); +// "constantFrom" accepts arguments not having the same types without any typing trick +expectTypeOf(fc.constantFrom(1, 2, 'hello')).toEqualTypeOf>(); +// "as const" was a way to prevent extra simplification of "constantFrom" +expectTypeOf(fc.constantFrom(...([1, 2, 'hello'] as const))).toEqualTypeOf>(); // uniqueArray arbitrary -expectType>()(fc.uniqueArray(fc.string()), 'simple arrays of unique values'); -expectType>()( +// simple arrays of unique values +expectTypeOf(fc.uniqueArray(fc.string())).toEqualTypeOf>(); +// arrays of unique values based on a selector +expectTypeOf( fc.uniqueArray(fc.record({ name: fc.string() }), { selector: (item) => item.name, }), - 'arrays of unique values based on a selector', -); -expectType>()( +).toEqualTypeOf>(); +// arrays of unique values using a custom comparison function +expectTypeOf( fc.uniqueArray(fc.record({ name: fc.string() }), { comparator: (itemA, itemB) => itemA.name === itemB.name, }), - 'arrays of unique values using a custom comparison function', -); -expectType>()( +).toEqualTypeOf>(); +// arrays of unique values using a custom comparison function and a complex selector (need an explicit typing) +expectTypeOf( fc.uniqueArray(fc.record({ name: fc.string() }), { // Ideally we should not need to explicitely type `itemA`, but so far it is needed comparator: (itemA: { toto: string }, itemB) => itemA.toto === itemB.toto, selector: (item) => ({ toto: item.name }), }), - 'arrays of unique values using a custom comparison function and a complex selector (need an explicit typing)', -); +).toEqualTypeOf>(); +// arrays of unique values accept the aggregated type as input declare const constraintsUniqueArray1: fc.UniqueArrayConstraints<{ name: string }, { toto: string }>; -expectType>()( - fc.uniqueArray(fc.record({ name: fc.string() }), constraintsUniqueArray1), - 'arrays of unique values accept the aggregated type as input', -); +expectTypeOf(fc.uniqueArray(fc.record({ name: fc.string() }), constraintsUniqueArray1)).toEqualTypeOf< + fc.Arbitrary<{ name: string }[]> +>(); fc.uniqueArray(fc.record({ name: fc.string() }), { // @ts-expect-error - Custom comparison function is not compatible with default selector @@ -178,14 +162,12 @@ fc.uniqueArray(fc.record({ name: fc.string() }), { // record arbitrary declare const mySymbol1: unique symbol; declare const mySymbol2: unique symbol; -expectType>()( - fc.record({ a: fc.nat(), b: fc.string() }), - '"record" can contain multiple types', -); -expectType>()( - fc.record({ [mySymbol1]: fc.nat(), [mySymbol2]: fc.string() }), - '"record" can be indexed using unique symbols as keys', -); +// "record" can contain multiple types +expectTypeOf(fc.record({ a: fc.nat(), b: fc.string() })).toEqualTypeOf>(); +// "record" can be indexed using unique symbols as keys +expectTypeOf(fc.record({ [mySymbol1]: fc.nat(), [mySymbol2]: fc.string() })).toEqualTypeOf< + fc.Arbitrary<{ [mySymbol1]: number; [mySymbol2]: string }> +>(); // Related to https://github.com/microsoft/TypeScript/issues/27525: //expectType>()( // fc.record({ [Symbol.iterator]: fc.nat(), [mySymbol2]: fc.string() }), @@ -197,148 +179,132 @@ expectType>()( // fc.record({ [symbolIterator]: fc.nat(), [mySymbol2]: fc.string() }), // '"record" can be indexed using known symbols as keys based on a workaround' //); -expectType>()( - fc.record({ a: fc.nat(), b: fc.string() }, {}), - '"record" accepts empty constraints', -); -expectType>()( - fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: [] }), - '"record" only applies optional on keys declared within requiredKeys even when empty', -); -expectType>()( - fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: ['a'] }), - '"record" only applies optional on keys declared within requiredKeys even if unique', -); -expectType>()( - fc.record({ a: fc.nat(), b: fc.string(), c: fc.string() }, { requiredKeys: ['a', 'c'] }), - '"record" only applies optional on keys declared within requiredKeys even if multiple ones specified', -); -// prettier-ignore -// @fc-expect-error-require-exactOptionalPropertyTypes -expectType>()(fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: [] }), '"record" only applies optional on keys declared within requiredKeys by adding ? without |undefined'); -// prettier-ignore-end -expectType>()( +// "record" accepts empty constraints +expectTypeOf(fc.record({ a: fc.nat(), b: fc.string() }, {})).toEqualTypeOf>(); +// "record" only applies optional on keys declared within requiredKeys even when empty +expectTypeOf(fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: [] })).toEqualTypeOf< + fc.Arbitrary<{ a?: number; b?: string }> +>(); +// "record" only applies optional on keys declared within requiredKeys even if unique +expectTypeOf(fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: ['a'] })).toEqualTypeOf< + fc.Arbitrary<{ a: number; b?: string }> +>(); +// "record" only applies optional on keys declared within requiredKeys even if multiple ones specified +expectTypeOf(fc.record({ a: fc.nat(), b: fc.string(), c: fc.string() }, { requiredKeys: ['a', 'c'] })).toEqualTypeOf< + fc.Arbitrary<{ a: number; b?: string; c: string }> +>(); +// "record" only applies optional on keys declared within requiredKeys by adding ? without |undefined when exactOptionalPropertyTypes +// unfortunately expectTypeOf cannot capture such subtleties +expectTypeOf(fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: [] })).toEqualTypeOf< + fc.Arbitrary<{ a?: number; b?: string | undefined }> +>(); +// "record" only applies optional on keys declared within requiredKeys and preserves existing |undefined when adding ? +expectTypeOf( fc.record({ a: fc.nat(), b: fc.option(fc.string(), { nil: undefined }) }, { requiredKeys: [] }), - '"record" only applies optional on keys declared within requiredKeys and preserves existing |undefined when adding ?', -); -expectType>()( +).toEqualTypeOf>(); +// "record" only applies optional on keys declared within requiredKeys even if it contains symbols +expectTypeOf( fc.record({ [mySymbol1]: fc.nat(), [mySymbol2]: fc.string() }, { requiredKeys: [mySymbol1] as [typeof mySymbol1] }), - '"record" only applies optional on keys declared within requiredKeys even if it contains symbols', -); +).toEqualTypeOf>(); // Related to https://github.com/microsoft/TypeScript/issues/27525 //expectType>()( // fc.record({ [Symbol.iterator]: fc.nat(), [mySymbol2]: fc.string() }, { requiredKeys: [Symbol.iterator] }) //); // See workaround above -expectType>()( +// "record" only applies optional on keys declared within requiredKeys even if it contains symbols and normal keys +expectTypeOf( fc.record( { [mySymbol1]: fc.nat(), [mySymbol2]: fc.string(), a: fc.nat(), b: fc.string() }, { requiredKeys: [mySymbol1, 'a'] as [typeof mySymbol1, 'a'] }, ), - '"record" only applies optional on keys declared within requiredKeys even if it contains symbols and normal keys', -); +).toEqualTypeOf>(); type Query = { data: { field: 'X' } }; -expectType>()( - // issue 1453 - fc.record({ data: fc.record({ field: fc.constant('X') }) }), - '"record" can be passed the requested type in <*>', -); -expectType>>()( - // issue 1453 - fc.record>({ data: fc.record({ field: fc.constant('X') }) }), - '"record" can be passed something assignable to the requested type in <*>', -); +// "record" can be passed the requested type in <*> +// issue 1453 +expectTypeOf(fc.record({ data: fc.record({ field: fc.constant('X') }) })).toEqualTypeOf>(); +// "record" can be passed something assignable to the requested type in <*> +// issue 1453 +expectTypeOf(fc.record>({ data: fc.record({ field: fc.constant('X') }) })).toEqualTypeOf< + fc.Arbitrary> +>(); // @ts-expect-error - requiredKeys references an unknown key fc.record({ a: fc.nat(), b: fc.string() }, { requiredKeys: ['c'] }); // @ts-expect-error - record expects arbitraries not raw values fc.record({ a: 1 }); // dictionary arbitrary -expectType>>()( - fc.dictionary(fc.string(), fc.nat()), - 'String key call to "dictionary"', -); -expectType>>()( - fc.dictionary(fc.string(), fc.nat()), - 'String key call to "dictionary" with single generic', -); -expectType>>()( - fc.dictionary(fc.string(), fc.nat()), - 'String key call to "dictionary" with two generics', -); -expectType>>()( - fc.dictionary(fc.nat(), fc.nat()), - 'Number key call to "dictionary"', -); -expectType>>()( - fc.dictionary(fc.nat(), fc.nat()), - 'Number key call to "dictionary" with generics', -); -expectType>>()( - fc.dictionary(fc.string().map(Symbol), fc.nat()), - 'Symbol key call to "dictionary"', -); -expectType>>()( - fc.dictionary(fc.string().map(Symbol), fc.nat()), - 'Symbol key call to "dictionary" with generics', -); +// String key call to "dictionary" +expectTypeOf(fc.dictionary(fc.string(), fc.nat())).toEqualTypeOf>>(); +// String key call to "dictionary" with single generic +expectTypeOf(fc.dictionary(fc.string(), fc.nat())).toEqualTypeOf>>(); +// String key call to "dictionary" with two generics +expectTypeOf(fc.dictionary(fc.string(), fc.nat())).toEqualTypeOf< + fc.Arbitrary> +>(); +// Number key call to "dictionary" +expectTypeOf(fc.dictionary(fc.nat(), fc.nat())).toEqualTypeOf>>(); +// Number key call to "dictionary" with generics +expectTypeOf(fc.dictionary(fc.nat(), fc.nat())).toEqualTypeOf>>(); +// Symbol key call to "dictionary" +expectTypeOf(fc.dictionary(fc.string().map(Symbol), fc.nat())).toEqualTypeOf>>(); +// Symbol key call to "dictionary" with generics +expectTypeOf(fc.dictionary(fc.string().map(Symbol), fc.nat())).toEqualTypeOf< + fc.Arbitrary> +>(); // @ts-expect-error - dictionary expects arbitraries producing PropertyKey for keys fc.dictionary(fc.anything(), fc.string()); // map arbitrary -expectType>>()(fc.map(fc.string(), fc.nat()), 'Basic call to "map"'); -expectType>>()( - fc.map(fc.string(), fc.nat()), - 'Call to "map" with generics', -); +// Basic call to "map" +expectTypeOf(fc.map(fc.string(), fc.nat())).toEqualTypeOf>>(); +// Call to "map" with generics +expectTypeOf(fc.map(fc.string(), fc.nat())).toEqualTypeOf>>(); // tuple arbitrary -expectType>()(fc.tuple(), '"tuple" with zero argument'); -expectType>()(fc.tuple(fc.nat()), '"tuple" with a single argument'); -expectType>()(fc.tuple(fc.nat(), fc.string()), '"tuple" with multiple arguments'); -expectType>()(fc.tuple(...([] as fc.Arbitrary[])), '"tuple" with spread arrays'); +// "tuple" with zero argument +expectTypeOf(fc.tuple()).toEqualTypeOf>(); +// "tuple" with a single argument +expectTypeOf(fc.tuple(fc.nat())).toEqualTypeOf>(); +// "tuple" with multiple arguments +expectTypeOf(fc.tuple(fc.nat(), fc.string())).toEqualTypeOf>(); +// "tuple" with spread arrays +expectTypeOf(fc.tuple(...([] as fc.Arbitrary[]))).toEqualTypeOf>(); // @ts-expect-error - tuple expects arbitraries not raw values fc.tuple(fc.nat(), ''); // oneof arbitrary -expectType>()( - fc.oneof(fc.string(), fc.string({ unit: 'binary' })), - '"oneof" with multiple arguments having the same type', -); -expectType>()( - fc.oneof(fc.string(), fc.nat()), - '"oneof" with multiple arguments of different types', -); -expectType>()( - fc.oneof({}, fc.string(), fc.nat()), - '"oneof" with different types and empty constraints', -); -expectType>()( - fc.oneof({ withCrossShrink: true }, fc.string(), fc.nat()), - '"oneof" with different types and some constraints', -); -expectType>()( +// "oneof" with multiple arguments having the same type +expectTypeOf(fc.oneof(fc.string(), fc.string({ unit: 'binary' }))).toEqualTypeOf>(); +// "oneof" with multiple arguments of different types +expectTypeOf(fc.oneof(fc.string(), fc.nat())).toEqualTypeOf>(); +// "oneof" with different types and empty constraints +expectTypeOf(fc.oneof({}, fc.string(), fc.nat())).toEqualTypeOf>(); +// "oneof" with different types and some constraints +expectTypeOf(fc.oneof({ withCrossShrink: true }, fc.string(), fc.nat())).toEqualTypeOf>(); +// "oneof" with weighted arbitraries and multiple arguments having the same type +expectTypeOf( fc.oneof({ arbitrary: fc.string(), weight: 1 }, { arbitrary: fc.string({ unit: 'binary' }), weight: 1 }), - '"oneof" with weighted arbitraries and multiple arguments having the same type', -); -expectType>()( - fc.oneof({ arbitrary: fc.string(), weight: 1 }, { arbitrary: fc.nat(), weight: 1 }), - '"oneof" with weighted arbitraries and multiple arguments of different types', -); -expectType>()( - fc.oneof({}, { arbitrary: fc.string(), weight: 1 }, { arbitrary: fc.nat(), weight: 1 }), - '"oneof" with weighted arbitraries and different types and empty constraints', -); -expectType>()( +).toEqualTypeOf>(); +// "oneof" with weighted arbitraries and multiple arguments of different types +expectTypeOf(fc.oneof({ arbitrary: fc.string(), weight: 1 }, { arbitrary: fc.nat(), weight: 1 })).toEqualTypeOf< + fc.Arbitrary +>(); +// "oneof" with weighted arbitraries and different types and empty constraints +expectTypeOf(fc.oneof({}, { arbitrary: fc.string(), weight: 1 }, { arbitrary: fc.nat(), weight: 1 })).toEqualTypeOf< + fc.Arbitrary +>(); +// "oneof" with weighted arbitraries and different types and some constraints +expectTypeOf( fc.oneof({ withCrossShrink: true }, { arbitrary: fc.string(), weight: 1 }, { arbitrary: fc.nat(), weight: 1 }), - '"oneof" with weighted arbitraries and different types and some constraints', -); -expectType>()( - fc.oneof({ withCrossShrink: true }, { arbitrary: fc.string(), weight: 1 }, fc.nat()), - '"oneof" with weighted arbitraries and non-weighted arbitraries', -); -expectType>()(fc.oneof(...([] as fc.Arbitrary[])), '"oneof" from array of arbitraries'); -expectType>()(fc.oneof(), '"oneof" must receive at least one arbitrary'); +).toEqualTypeOf>(); +// "oneof" with weighted arbitraries and non-weighted arbitraries +expectTypeOf(fc.oneof({ withCrossShrink: true }, { arbitrary: fc.string(), weight: 1 }, fc.nat())).toEqualTypeOf< + fc.Arbitrary +>(); +// "oneof" from array of arbitraries +expectTypeOf(fc.oneof(...([] as fc.Arbitrary[]))).toEqualTypeOf>(); +// "oneof" must receive at least one arbitrary +expectTypeOf(fc.oneof()).toEqualTypeOf>(); // @ts-expect-error - oneof expects arbitraries not raw values fc.oneof(fc.string(), '1'); @@ -346,161 +312,163 @@ fc.oneof(fc.string(), '1'); fc.oneof({ arbitrary: fc.string(), weight: 1 }, { arbitrary: '1', weight: 1 }); // option arbitrary -expectType>()(fc.option(fc.nat()), '"option" without any constraints'); -expectType>()( - fc.option(fc.nat(), { nil: null }), - '"option" with nil overriden to null (the original default)', -); -expectType>()( - fc.option(fc.nat(), { nil: 'custom_default' as const }), - '"option" with nil overriden to custom value', -); +// "option" without any constraints +expectTypeOf(fc.option(fc.nat())).toEqualTypeOf>(); +// "option" with nil overridden to null (the original default) +expectTypeOf(fc.option(fc.nat(), { nil: null })).toEqualTypeOf>(); +// "option" with nil overridden to custom value +expectTypeOf(fc.option(fc.nat(), { nil: 'custom_default' as const })).toEqualTypeOf< + fc.Arbitrary +>(); // @ts-expect-error - option expects arbitraries not raw values fc.option(1); // tie arbitrary -expectType<{}>()( - fc.letrec((_tie) => ({})), - 'Empty "letrec"', -); -expectType<{}>()( - fc.letrec<{}>((_tie) => ({})), - 'Empty "letrec" with types manually defined', -); -expectType<{ a: fc.Arbitrary; b: fc.Arbitrary }>()( +// Empty "letrec" +expectTypeOf(fc.letrec((_tie) => ({}))).toEqualTypeOf<{}>(); +// Empty "letrec" with types manually defined +expectTypeOf(fc.letrec<{}>((_tie) => ({}))).toEqualTypeOf<{}>(); +// No recursion "letrec" +expectTypeOf( fc.letrec((_tie) => ({ a: fc.nat(), b: fc.string(), })), - 'No recursion "letrec"', -); -expectType<{ a: fc.Arbitrary; b: fc.Arbitrary }>()( +).toEqualTypeOf<{ a: fc.Arbitrary; b: fc.Arbitrary }>(); +// No recursion "letrec" with types manually defined +expectTypeOf( fc.letrec<{ a: number; b: string }>((_tie) => ({ a: fc.nat(), b: fc.string(), })), - 'No recursion "letrec" with types manually defined', -); -expectType<{ a: fc.Arbitrary; b: fc.Arbitrary }>()( +).toEqualTypeOf<{ a: fc.Arbitrary; b: fc.Arbitrary }>(); +// Recursive "letrec" +// TODO Typings should be improved: b type might be inferred from a +expectTypeOf( fc.letrec((tie) => ({ a: fc.nat(), b: tie('a'), })), - 'Recursive "letrec"', -); // TODO Typings should be improved: b type might be infered from a -expectType<{ a: fc.Arbitrary; b: fc.Arbitrary }>()( +).toEqualTypeOf<{ a: fc.Arbitrary; b: fc.Arbitrary }>(); +// Recursive "letrec" with types manually defined +expectTypeOf( fc.letrec<{ a: number; b: number }>((tie) => ({ a: fc.nat(), b: tie('a'), })), - 'Recursive "letrec" with types manually defined', -); -expectType<{ a: fc.Arbitrary; b: fc.Arbitrary }>()( +).toEqualTypeOf<{ a: fc.Arbitrary; b: fc.Arbitrary }>(); +// Invalid recursion "letrec" +// TODO Typings should be improved: referencing an undefined key should fail +expectTypeOf( fc.letrec((tie) => ({ a: fc.nat(), b: tie('c'), })), - 'Invalid recursion "letrec"', -); // TODO Typings should be improved: referencing an undefined key should failed -expectType<{ a: fc.Arbitrary; b: fc.Arbitrary }>()( +).toEqualTypeOf<{ a: fc.Arbitrary; b: fc.Arbitrary }>(); +// Invalid recursion "letrec" with types manually defined +// TODO Even when fully typed we accept undeclared keys from being used on tie (see why PR-2968) +expectTypeOf( fc.letrec<{ a: number; b: unknown }>((tie) => ({ a: fc.nat(), b: tie('c'), })), - 'Invalid recursion "letrec" with types manually defined', -); // TODO Even when fully typed we accept undeclared keys from being used on tie (see why PR-2968) -expectType<{ a: fc.Arbitrary }>()( +).toEqualTypeOf<{ a: fc.Arbitrary; b: fc.Arbitrary }>(); +// Accept additional keys within "letrec" but do not expose them outside +expectTypeOf( fc.letrec<{ a: number }>((_tie) => ({ a: fc.nat(), b: fc.nat(), })), - 'Accept additional keys within "letrec" but do not expose them outside', -); +).toEqualTypeOf<{ a: fc.Arbitrary }>(); fc.letrec<{ a: string }>((_tie) => ({ // @ts-expect-error - reject builders implying 'wrongly typed keys' when type declared a: fc.nat(), })); // clone arbitrary -expectType>()(fc.clone(fc.nat(), 0), '"clone" 0-time'); -expectType>()(fc.clone(fc.nat(), 1), '"clone" 1-time'); -expectType>()(fc.clone(fc.nat(), 2), '"clone" 2-times'); -expectType>()(fc.clone(fc.nat(), 3), '"clone" 3-times'); -expectType>()(fc.clone(fc.nat(), 4), '"clone" 4-times'); -expectType>()(fc.clone(fc.nat(), 5), '"clone" 5-times'); +// "clone" 0-time +expectTypeOf(fc.clone(fc.nat(), 0)).toEqualTypeOf>(); +// "clone" 1-time +expectTypeOf(fc.clone(fc.nat(), 1)).toEqualTypeOf>(); +// "clone" 2-times +expectTypeOf(fc.clone(fc.nat(), 2)).toEqualTypeOf>(); +// "clone" 3-times +expectTypeOf(fc.clone(fc.nat(), 3)).toEqualTypeOf>(); +// "clone" 4-times +expectTypeOf(fc.clone(fc.nat(), 4)).toEqualTypeOf>(); +// "clone" 5-times +expectTypeOf(fc.clone(fc.nat(), 5)).toEqualTypeOf>(); declare const nTimesClone: number; -expectType>()(fc.clone(fc.nat(), nTimesClone), '"clone" with non-precise number of times'); +// "clone" with non-precise number of times +expectTypeOf(fc.clone(fc.nat(), nTimesClone)).toEqualTypeOf>(); // func arbitrary -expectType number>>()(fc.func(fc.nat()), '"func" producing "nat"'); +// "func" producing "nat" +expectTypeOf(fc.func(fc.nat())).toMatchTypeOf number>>(); // @ts-expect-error - func expects arbitraries not raw values fc.func(1); -// falsy arbitary -expectType>()( - fc.falsy(), - 'falsy" without any constraints', -); -expectType>()( - fc.falsy({}), - 'falsy" with empty constraints', -); -expectType>()( - fc.falsy({ withBigInt: false }), - 'falsy" with withBigInt=false', -); -expectType>()( - fc.falsy({ withBigInt: true }), - 'falsy" with withBigInt=true', -); +// falsy arbitrary +// "falsy" without any constraints +expectTypeOf(fc.falsy()).toEqualTypeOf>(); +// "falsy" with empty constraints +expectTypeOf(fc.falsy({})).toEqualTypeOf>(); +// "falsy" with withBigInt=false +expectTypeOf(fc.falsy({ withBigInt: false })).toEqualTypeOf< + fc.Arbitrary +>(); +// "falsy" with withBigInt=true +expectTypeOf(fc.falsy({ withBigInt: true })).toEqualTypeOf< + fc.Arbitrary +>(); // configureGlobal -expectType()( - fc.configureGlobal({ reporter: (_out: fc.RunDetails) => {} }), - '"configureGlobal" with custom reporter', -); +// "configureGlobal" with custom reporter +expectTypeOf(fc.configureGlobal({ reporter: (_out: fc.RunDetails) => {} })).toEqualTypeOf(); // FIXME // @ts-expect-error - reporter cannot be defined with precise type on configureGlobal //fc.configureGlobal({ reporter: (out: fc.RunDetails<[number]>) => {} }); // entityGraph type Node = { name: string; linkTo: Node[] }; -expectType>()( +// translate "many" into an array +expectTypeOf( fc.entityGraph( { node: { name: fc.string() } }, { node: { linkTo: { arity: 'many', type: 'node' } } }, { unicityConstraints: { node: (n) => n.name } }, ), - 'translate "many" into an array', -); +).toEqualTypeOf>(); type Employee = { firstName: string; lastName: string; manager: Employee | undefined }; -expectType>()( +// translate "0-1" into a defined value or undefined +expectTypeOf( fc.entityGraph( { employee: { firstName: fc.string(), lastName: fc.string() } }, { employee: { manager: { arity: '0-1', type: 'employee', strategy: 'successor' } } }, ), - 'translate "0-1" into a defined value or undefined', -); +).toEqualTypeOf>(); type Profile = { id: string }; type User = { userName: string; profile: Profile }; -expectType>()( +// translate "1" into a defined value +expectTypeOf( fc.entityGraph( { user: { userName: fc.string() }, profile: { id: fc.uuid() } }, { user: { profile: { arity: '1', type: 'profile', strategy: 'exclusive' } }, profile: {} }, { unicityConstraints: { user: (u) => { - expectType>()(u, 'non-relational part of an User'); + // non-relational part of an User + expectTypeOf(u).toEqualTypeOf>(); return u.userName; }, profile: (p) => { - expectType()(p, 'non-relational part of a Profile'); + // non-relational part of a Profile + expectTypeOf(p).toEqualTypeOf(); return p.id; }, }, }, ), - 'translate "1" into a defined value', -); +).toEqualTypeOf>(); fc.entityGraph( { user: { userName: fc.string() }, profile: { id: fc.uuid() } }, // @ts-expect-error - Expect all entities to be declared for relations @@ -524,7 +492,8 @@ fc.entityGraph( ); type EmployeeWithTeam = { firstName: string; lastName: string; team: TeamWithEmployees }; type TeamWithEmployees = { name: string; members: EmployeeWithTeam[] }; -expectType>()( +// support inverse relationships +expectTypeOf( fc.entityGraph( { employee: { firstName: fc.string(), lastName: fc.string() }, @@ -535,5 +504,4 @@ expectType>(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ee491d2..f115a419 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,9 +157,6 @@ importers: specifier: ^7.0.0 version: 7.0.1 devDependencies: - '@fast-check/expect-type': - specifier: workspace:* - version: link:../expect-type '@fast-check/poisoning': specifier: workspace:* version: link:../poisoning