Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for TypeScript v4.x #56

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
"ts-node": "^8.9.1",
"ts-type-checked": "file:../../dist",
"ttypescript": "^1.5.10",
"typescript": "^3.9.6"
"typescript": "^4.0.7"
}
}
2 changes: 1 addition & 1 deletion examples/mocha/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"ts-node": "8.10.2",
"ts-type-checked": "file:../../dist",
"ttypescript": "^1.5.10",
"typescript": "^3.9.6"
"typescript": "^4.0.7"
}
}
2 changes: 1 addition & 1 deletion examples/rollup/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.27.0",
"ts-type-checked": "file:../../dist",
"typescript": "^3.9.6"
"typescript": "^4.0.7"
}
}
2 changes: 1 addition & 1 deletion examples/ts-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"devDependencies": {
"ts-type-checked": "file:../../dist",
"ttypescript": "^1.5.10",
"typescript": "^3.9.6"
"typescript": "^4.0.7"
},
"dependencies": {
"ts-node": "^8.10.2"
Expand Down
2 changes: 1 addition & 1 deletion examples/ttypescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
"devDependencies": {
"ts-type-checked": "file:../../dist",
"ttypescript": "^1.5.10",
"typescript": "^3.9.6"
"typescript": "^4.0.7"
}
}
10 changes: 5 additions & 5 deletions examples/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6164,7 +6164,7 @@ ts-node@8.10.2, ts-node@^8.10.2, ts-node@^8.9.1:
yn "3.1.1"

"ts-type-checked@file:../dist":
version "0.5.0"
version "0.6.3"

tslib@1.11.2:
version "1.11.2"
Expand Down Expand Up @@ -6244,10 +6244,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=

typescript@^3.9.6:
version "3.9.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
typescript@^4.0.7:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==

ua-parser-js@^0.7.21:
version "0.7.21"
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
"prettier": "^2.0.5",
"rollup": "^2.20.0",
"rollup-plugin-copy": "^3.3.0",
"typescript": "^3.9.6"
"ts-reflection": "^0.3.0",
"ttypescript": "^1.5.12",
"typescript": "^4.0.7"
},
"husky": {
"hooks": {
Expand Down
7 changes: 6 additions & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import copy from 'rollup-plugin-copy';
import resolve from '@rollup/plugin-node-resolve';
import ts from '@wessberg/rollup-plugin-ts';
import tsReflection from 'ts-reflection/transformer';

const defaults = {
external: ['child_process', 'path', 'typescript'],
plugins: [
resolve({ preferBuiltins: true }),
ts(),
ts({ transformers: [
({ program }) => ({
before: tsReflection(program),
}),
], }),
copy({
targets: [{ src: ['package.json', 'LICENSE', 'README.md'], dest: './dist' }],
}),
Expand Down
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@rollup/plugin-node-resolve": "^8.1.0",
"@wessberg/rollup-plugin-ts": "^1.2.27",
"rollup": "^2.20.0",
"typescript": "^3.9.6"
"typescript": "^4.0.7"
},
"peerDependencies": {
"typescript": ">=2.7.2"
Expand Down
44 changes: 33 additions & 11 deletions src/transformer/typeDescriptor/typeDescriptorGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
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 { typeFlags } from '../utils/debug';
import ts from 'typescript';

/**
Expand Down Expand Up @@ -95,14 +96,14 @@ 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
if (assert.isTrueKeyword(typeNode)) return { _type: 'literal', value: ts.createTrue() };
if (assert.isTrue(type, typeNode)) return { _type: 'literal', value: ts.createTrue() };

// False
if (assert.isFalseKeyword(typeNode)) return { _type: 'literal', value: ts.createFalse() };
if (assert.isFalse(type, typeNode)) return { _type: 'literal', value: ts.createFalse() };

// Promise
//
Expand All @@ -120,14 +121,11 @@ export const createTypeDescriptorGenerator = (program: ts.Program, logger: Logge

// Literal types
if (assert.isLiteral(type)) {
logger.debug('Literal');

const value = (type as ts.LiteralType).value;
if (value === undefined) {
if (type.value === undefined) {
throw new Error('Could not find value for a literal type ' + typeName);
}

return { _type: 'literal', value: ts.createLiteral(value) };
return { _type: 'literal', value: ts.createLiteral(type.value) };
}

// Intersection
Expand Down Expand Up @@ -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<TypeName | [TypeName]>(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',
Expand Down
24 changes: 20 additions & 4 deletions src/transformer/typeDescriptor/utils/assert.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LibraryTypeDescriptorName } from './getLibraryTypeDescriptorName';
import ts from 'typescript';
import ts, { isLiteralTypeNode } from 'typescript';

export const isBigInt = (type: ts.Type, libraryDescriptorName?: LibraryTypeDescriptorName): boolean =>
!!(type.flags & ts.TypeFlags.BigInt) || libraryDescriptorName === 'BigInt';
Expand Down Expand Up @@ -61,18 +61,34 @@ export const isTrueKeyword = (typeNode: ts.TypeNode | undefined): boolean =>
export const isFalseKeyword = (typeNode: ts.TypeNode | undefined): boolean =>
typeNode?.kind === ts.SyntaxKind.FalseKeyword;

export const isTuple = (type: ts.Type, typeNode: ts.TypeNode | undefined): type is ts.TupleType =>
export const isTuple = (type: ts.Type, typeNode?: ts.TypeNode): type is ts.TupleType =>
typeNode?.kind === ts.SyntaxKind.TupleType;

export const isIntersection = (type: ts.Type, typeNode: ts.TypeNode | undefined): type is ts.IntersectionType =>
export const isIntersection = (type: ts.Type, typeNode?: ts.TypeNode): type is ts.IntersectionType =>
(typeof type.isIntersection === 'function' && type.isIntersection()) ||
typeNode?.kind === ts.SyntaxKind.IntersectionType;

export const isUnion = (type: ts.Type, typeNode: ts.TypeNode | undefined): type is ts.UnionType =>
export const isUnion = (type: ts.Type, typeNode?: ts.TypeNode): type is ts.UnionType =>
(typeof type.isUnion === 'function' && type.isUnion()) ||
typeNode?.kind === ts.SyntaxKind.UnionType ||
!!(type.getFlags() & ts.TypeFlags.Union);

export const isFalse = (type: ts.Type, typeNode?: ts.TypeNode): type is ts.LiteralType => {
if (isFalseKeyword(typeNode)) return true;
if (isLiteral(type) && (type.value as unknown) === false) return true;
if (typeNode && isLiteralTypeNode(typeNode) && typeNode.literal.kind === ts.SyntaxKind.FalseKeyword) return true;

return false;
}

export const isTrue = (type: ts.Type, typeNode?: ts.TypeNode): type is ts.LiteralType => {
if (isTrueKeyword(typeNode)) return true;
if (isLiteral(type) && (type.value as unknown) === true) return true;
if (typeNode && isLiteralTypeNode(typeNode) && typeNode.literal.kind === ts.SyntaxKind.TrueKeyword) return true;

return false;
}

export const isClassOrInterface = (type: ts.Type, typeNode?: ts.TypeNode): type is ts.InterfaceType =>
(typeof type.isClassOrInterface === 'function' && type.isClassOrInterface()) ||
typeNode?.kind === ts.SyntaxKind.InterfaceDeclaration ||
Expand Down
3 changes: 1 addition & 2 deletions src/transformer/typeGuard/utils/codeGenerators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
2 changes: 1 addition & 1 deletion src/transformer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export interface ArrayTypeDescriptor {

export interface TupleTypeDescriptor {
_type: 'tuple';
types: TypeName[];
types: Array<TypeName | [TypeName]>;
}

export interface PromiseTypeDescriptor {
Expand Down
17 changes: 11 additions & 6 deletions src/transformer/utils/debug.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ts from 'typescript';
import { propertiesOf } from 'ts-reflection';

/**
* Helper debugging function that takes a type as a parameter and returns
Expand All @@ -9,11 +10,15 @@ import ts from 'typescript';
* @returns {String[]} Array of type flags names
*/
export const typeFlags = (type: ts.Type): string[] => {
return Object.keys(ts.TypeFlags).filter(
(flagName) => !!((ts.TypeFlags[flagName as ts.TypeFlags] as number) & type.flags),
return propertiesOf<typeof ts.TypeFlags>().filter(
(flagName) => !!(ts.TypeFlags[flagName] & type.flags),
);
};

export const kindOf = (typeNode: ts.TypeNode): string => {
return ts.SyntaxKind[typeNode.kind];
};

/**
* Helper debugging function that takes a type as a parameter and returns
* a human-readable list of its object flags (if it has any)
Expand All @@ -26,8 +31,8 @@ export const objectFlags = (type: ts.Type): string[] => {
const objectFlags = (type as ts.TypeReference).objectFlags;
if (typeof objectFlags !== 'number') return [];

return Object.keys(ts.ObjectFlags).filter(
(flagName) => !!((ts.ObjectFlags[flagName as ts.ObjectFlags] as number) & objectFlags),
return propertiesOf<typeof ts.ObjectFlags>().filter(
(flagName) => !!(ts.ObjectFlags[flagName] & objectFlags),
);
};

Expand All @@ -40,7 +45,7 @@ export const objectFlags = (type: ts.Type): string[] => {
* @returns {String[]} Array of symbol flags names
*/
export const symbolFlags = (symbol: ts.Symbol): string[] => {
return Object.keys(ts.SymbolFlags).filter(
(flagName) => !!((ts.SymbolFlags[flagName as ts.SymbolFlags] as number) & symbol.flags),
return propertiesOf<typeof ts.SymbolFlags>().filter(
(flagName) => !!(ts.SymbolFlags[flagName] & symbol.flags),
);
};
2 changes: 1 addition & 1 deletion test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"ts-node": "^8.10.2",
"tslib": "^2.0.0",
"ttypescript": "^1.5.10",
"typescript": "^3.9.6"
"typescript": "4.0.7"
},
"workspaces": [
"setups/*"
Expand Down
2 changes: 1 addition & 1 deletion test/scripts/test-with-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ for MATCHING_SETUP in $MATCHING_SETUPS; do
yarn workspace "$MATCHING_SETUP" run jest "$TEST_PATTERN"
else
# In debug mode node is started with inspect flag
yarn workspace "$MATCHING_SETUP" node --inspect-brk $(which jest) "$TEST_PATTERN" --runInBand
yarn workspace "$MATCHING_SETUP" node --inspect $(which jest) "$TEST_PATTERN" --runInBand
fi

printf "\n\n\n"
Expand Down
5 changes: 4 additions & 1 deletion test/scripts/versions.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
4.2.3
4.1.5
4.0.7
3.9.2
3.8.2
3.7.2
Expand All @@ -10,4 +13,4 @@
3.0.1
2.9.1
2.8.3
2.7.2
2.7.2
1 change: 1 addition & 0 deletions test/setups/typescript--4.0.7/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../../jest.config');
11 changes: 11 additions & 0 deletions test/setups/typescript--4.0.7/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@ts-type-checked/test--typescript--4.0.7",
"version": "0.0.1",
"private": true,
"peerDependencies": {
"typescript": ">=4.0.7"
},
"dependencies": {
"ts-type-checked": "file:../../../dist"
}
}
21 changes: 21 additions & 0 deletions test/setups/typescript--4.0.7/tests/tuples.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'jest';

import { assert, notNullOrUndefined, nullable, numeric, oneOf, primitive } from '../../../utils/utils.v2';
import { isA, typeCheckFor } from 'ts-type-checked';
import fc from 'fast-check';

describe('Tuples', () => {
test('Labelled tuple elements', () => {
type TypeReference1 = [start: number, end: string];

const validArbitrary: fc.Arbitrary<TypeReference1> = fc.tuple(numeric(), fc.string());
const invalidArbitrary = oneOf<unknown>(
primitive(),
nullable(),
fc.constantFrom(['hey', NaN]),
fc.tuple(fc.string(), fc.string())
);

assert(validArbitrary, invalidArbitrary, [typeCheckFor<TypeReference1>(), (value) => isA<TypeReference1>(value)]);
});
});
7 changes: 7 additions & 0 deletions test/setups/typescript--4.0.7/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"target": "ESNext"
}
}
1 change: 1 addition & 0 deletions test/setups/typescript--4.1.5/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../../jest.config');
14 changes: 14 additions & 0 deletions test/setups/typescript--4.1.5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@ts-type-checked/test--typescript--4.1.5",
"version": "0.0.1",
"private": true,
"peerDependencies": {
"typescript": ">=4.1.5"
},
"dependencies": {
"ts-type-checked": "file:../../../dist"
},
"devDependencies": {
"ts-reflection": "^0.3.0"
}
}
Loading