diff --git a/src/transformer/typeDescriptor/typeDescriptorGenerator.ts b/src/transformer/typeDescriptor/typeDescriptorGenerator.ts index fb34c19..cd4a6a1 100644 --- a/src/transformer/typeDescriptor/typeDescriptorGenerator.ts +++ b/src/transformer/typeDescriptor/typeDescriptorGenerator.ts @@ -1,13 +1,13 @@ import * as assert from './utils/assert'; import { Logger } from '../utils/logger'; -import { TypeDescriptor } from '../types'; +import { TypeDescriptor, TypeName } from '../types'; import { TypeDescriptorGenerator, TypeDescriptorGeneratorCallback, TypeNameResolver } from '../types'; import { functionTypeWarning, promiseTypeWarning } from './utils/messages'; import { getDOMElementClassName } from './utils/getDOMElementClassName'; import { getLibraryTypeDescriptorName } from './utils/getLibraryTypeDescriptorName'; import { getPropertyTypeDescriptors } from './utils/getPropertyTypeDescriptors'; -import ts from 'typescript'; import { typeFlags } from '../utils/debug'; +import ts from 'typescript'; /** * A factory for TypeDescriptorGenerator functions. @@ -96,7 +96,7 @@ export const createTypeDescriptorGenerator = (program: ts.Program, logger: Logge if (assert.isNever(type)) return { _type: 'never' }; // For the checks below we need access to the TypeNode for this type - const typeNode = typeChecker.typeToTypeNode(type, scope); + const typeNode = typeChecker.typeToTypeNode(type, scope, undefined); const typeName = typeChecker.typeToString(type, scope); // True @@ -119,8 +119,6 @@ export const createTypeDescriptorGenerator = (program: ts.Program, logger: Logge }); } - - // Literal types if (assert.isLiteral(type)) { if (type.value === undefined) { @@ -157,11 +155,35 @@ export const createTypeDescriptorGenerator = (program: ts.Program, logger: Logge return { _type: 'keyword', value: 'object' }; } + if (typeNode && ts.isTupleTypeNode(typeNode)) { + const typeElementNodes = typeNode.elements; + const typeArguments = (type as ts.TupleType).typeArguments || []; + debugger; + + // const types = typeNode.elements.map(unitType => { + // // return resolve(scope, unitType.type) + // }); + + return (resolve: TypeNameResolver) => ({ + _type: 'tuple', + types: typeArguments.map((type, index) => { + const typeElementNode = typeElementNodes[index]; + if (ts.isRestTypeNode(typeElementNode)) { + console.warn('rest type node', typeName, index); + + return [resolve(scope, type)]; + } + + return resolve(scope, type); + }), + }); + } + // Tuple if (assert.isTuple(type, typeNode)) { - logger.debug('Tuple'); - const typeArguments = type.typeArguments || []; + typeArguments.forEach((arg) => console.log('type', typeFlags(arg))); + debugger; return (resolve: TypeNameResolver) => ({ _type: 'tuple', diff --git a/src/transformer/typeGuard/utils/codeGenerators.ts b/src/transformer/typeGuard/utils/codeGenerators.ts index 2d4b6ab..be527bb 100644 --- a/src/transformer/typeGuard/utils/codeGenerators.ts +++ b/src/transformer/typeGuard/utils/codeGenerators.ts @@ -49,12 +49,11 @@ export const createTupleTypeGuard = ( length: number, createElementCheck: (value: ts.Expression, index: number) => ts.Expression, ): ts.Expression => { - const arrayLengthCheck = ts.createStrictEquality(ts.createPropertyAccess(value, 'length'), ts.createLiteral(length)); const elementChecks = Array.from({ length }).map((_, index) => createElementCheck(ts.createElementAccess(value, index), index), ); - return createLogicalAndChain(createIsArray(value), arrayLengthCheck, ...elementChecks); + return createLogicalAndChain(createIsArray(value), ...elementChecks); }; const createIsNotNumeric = (value: ts.Expression): ts.Expression => diff --git a/src/transformer/types.ts b/src/transformer/types.ts index 3dcc358..635be5f 100644 --- a/src/transformer/types.ts +++ b/src/transformer/types.ts @@ -196,7 +196,7 @@ export interface ArrayTypeDescriptor { export interface TupleTypeDescriptor { _type: 'tuple'; - types: TypeName[]; + types: Array; } export interface PromiseTypeDescriptor { diff --git a/test/scripts/versions.txt b/test/scripts/versions.txt index 3a46796..841537c 100644 --- a/test/scripts/versions.txt +++ b/test/scripts/versions.txt @@ -1,3 +1,5 @@ +4.2.3 +4.1.5 4.0.7 3.9.2 3.8.2 diff --git a/test/setups/typescript--4.2.3/jest.config.js b/test/setups/typescript--4.2.3/jest.config.js new file mode 100644 index 0000000..990bd44 --- /dev/null +++ b/test/setups/typescript--4.2.3/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config'); diff --git a/test/setups/typescript--4.2.3/package.json b/test/setups/typescript--4.2.3/package.json new file mode 100644 index 0000000..607ecf7 --- /dev/null +++ b/test/setups/typescript--4.2.3/package.json @@ -0,0 +1,14 @@ +{ + "name": "@ts-type-checked/test--typescript--4.2.3", + "version": "0.0.1", + "private": true, + "peerDependencies": { + "typescript": ">=4.2.3" + }, + "dependencies": { + "ts-type-checked": "file:../../../dist" + }, + "devDependencies": { + "ts-reflection": "^0.3.0" + } +} diff --git a/test/setups/typescript--4.2.3/tests/tuples.spec.ts b/test/setups/typescript--4.2.3/tests/tuples.spec.ts new file mode 100644 index 0000000..08d1947 --- /dev/null +++ b/test/setups/typescript--4.2.3/tests/tuples.spec.ts @@ -0,0 +1,45 @@ +import 'jest'; + +import { assert, notAnArray, notAnEmptyArray, notOfType, numeric, oneOf, primitive } from '../../../utils/utils.v2'; +import { isA, typeCheckFor } from 'ts-type-checked'; +import fc from 'fast-check'; + +describe('Tuples', () => { + // test('With optional elements', () => { + // type TypeReference1 = [string, number, boolean?]; + + // const validArbitrary: fc.Arbitrary = oneOf( + // fc.tuple(fc.string(), numeric()), + // fc.tuple(fc.string(), numeric(), fc.boolean()), + // ); + // const invalidArbitrary = oneOf( + // primitive(), + // fc.anything().filter(notAnArray), + // fc.array(numeric()), + // fc.tuple(fc.string(), fc.string()), + // fc.tuple(numeric(), numeric()), + // fc.tuple(fc.string(), numeric(), fc.anything().filter(notOfType('boolean'))), + // fc.constantFrom(["string", "string"], [6, 6], ["string", 7, 7]), + // ); + + // assert(validArbitrary, invalidArbitrary, [typeCheckFor(), (value) => isA(value)]); + // }); + + test('With rest elements at the beginning', () => { + type TypeReference1 = [...boolean[], string]; + + const validArbitrary: fc.Arbitrary = oneOf( + fc.tuple(fc.string()), + fc.array(fc.boolean()).chain((booleans) => fc.string().map((string) => [...booleans, string])), + ); + const invalidArbitrary = oneOf( + primitive(), + fc + .array(fc.anything().filter(notOfType('boolean')).filter(notAnEmptyArray)) + .chain((values) => fc.string().map((string) => [...values, string])), + fc.constantFrom([6, 'string']), + ); + + assert(validArbitrary, invalidArbitrary, [typeCheckFor(), (value) => isA(value)]); + }); +}); diff --git a/test/setups/typescript--4.2.3/tsconfig.json b/test/setups/typescript--4.2.3/tsconfig.json new file mode 100644 index 0000000..61a1baa --- /dev/null +++ b/test/setups/typescript--4.2.3/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "target": "ESNext" + } +} \ No newline at end of file