Skip to content

Commit

Permalink
feat: tssert subtypeOf and notSubtypeOf
Browse files Browse the repository at this point in the history
  • Loading branch information
skarab42 committed Jul 13, 2022
1 parent 027a1be commit ffbdfa6
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/api/tssert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ function notAssignableTo<TargetType>(_target?: unknown) {}
function identicalTo<TargetType>(_target?: unknown) {}
function notIdenticalTo<TargetType>(_target?: unknown) {}

function subtypeOf<TargetType>(_target?: unknown) {}
function notSubtypeOf<TargetType>(_target?: unknown) {}

expectType[API_PROPERTY_KEY] = 'tssert:expectType' as const;

assignableTo[API_PROPERTY_KEY] = 'tssert:assignableTo' as const;
Expand All @@ -18,12 +21,17 @@ notAssignableTo[API_PROPERTY_KEY] = 'tssert:notAssignableTo' as const;
identicalTo[API_PROPERTY_KEY] = 'tssert:identicalTo' as const;
notIdenticalTo[API_PROPERTY_KEY] = 'tssert:notIdenticalTo' as const;

subtypeOf[API_PROPERTY_KEY] = 'tssert:subtypeOf' as const;
notSubtypeOf[API_PROPERTY_KEY] = 'tssert:notSubtypeOf' as const;

const api = {
assignableTo,
identicalTo,
subtypeOf,
not: {
assignableTo: notAssignableTo,
identicalTo: notIdenticalTo,
subtypeOf: notSubtypeOf,
},
} as const;

Expand Down
6 changes: 6 additions & 0 deletions src/common/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export enum ErrorCode {
ASSERT_TYPE_NOT_IDENTICAL,
ASSERT_TYPE_IDENTICAL,
ASSERT_TYPE_TOO_WIDE,
ASSERT_TYPE_NOT_SUBTYPE,
ASSERT_TYPE_SUBTYPE,

ASSERT_ERROR,
ASSERT_DEPRECATED,
ASSERT_NOT_DEPRECATED,
Expand All @@ -40,6 +43,9 @@ export const errorMessages: Record<ErrorCode, string> = {
[ErrorCode.ASSERT_TYPE_NOT_IDENTICAL]: "Type '{expected}' is not identical to argument type '{argument}'.",
[ErrorCode.ASSERT_TYPE_IDENTICAL]: "Type '{expected}' is identical to argument type '{argument}'.",
[ErrorCode.ASSERT_TYPE_TOO_WIDE]: "Type '{expected}' is declared too wide for argument type '{argument}'.",
[ErrorCode.ASSERT_TYPE_NOT_SUBTYPE]: "Type '{expected}' is not subtype of argument type '{argument}'.",
[ErrorCode.ASSERT_TYPE_SUBTYPE]: "Type '{expected}' is subtype of argument type '{argument}'.",

[ErrorCode.ASSERT_ERROR]: 'An error is expected.',
[ErrorCode.ASSERT_DEPRECATED]: "Expected '{argument}' to be marked as '@deprecated'.",
[ErrorCode.ASSERT_NOT_DEPRECATED]: "Expected '{argument}' to not be marked as '@deprecated'.",
Expand Down
2 changes: 2 additions & 0 deletions src/plugin/assert/tssert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export * from './assignable-to';
export * from './not-assignable-to';
export * from './identical-to';
export * from './not-identical-to';
export * from './subtype-of';
export * from './not-subtype-of';
33 changes: 33 additions & 0 deletions src/plugin/assert/tssert/not-subtype-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type ts from 'unleashed-typescript';
import type { Assertion } from '../../types';
import { getTypes, typeError } from './util';
import { ErrorCode } from '../../../common/error';
import type { Compiler } from '../../../typescript/types';

export function notSubtypeOf({ node }: Assertion, { sourceFile, typeChecker }: Compiler): ts.Diagnostic | undefined {
const expectedNode = node.expression.getChildAt(0).getChildAt(0) as ts.CallExpression;
const expected = getTypes(expectedNode, typeChecker);

if (expected.diagnostic) {
return expected.diagnostic;
}

const argument = getTypes(node, typeChecker);

if (argument.diagnostic) {
return argument.diagnostic;
}

if (typeChecker.isTypeSubtypeOf(argument.type, expected.type)) {
return typeError(
ErrorCode.ASSERT_TYPE_SUBTYPE,
typeChecker,
argument.type,
expected.type,
sourceFile,
expectedNode,
);
}

return;
}
33 changes: 33 additions & 0 deletions src/plugin/assert/tssert/subtype-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type ts from 'unleashed-typescript';
import type { Assertion } from '../../types';
import { getTypes, typeError } from './util';
import { ErrorCode } from '../../../common/error';
import type { Compiler } from '../../../typescript/types';

export function subtypeOf({ node }: Assertion, { sourceFile, typeChecker }: Compiler): ts.Diagnostic | undefined {
const expectedNode = node.expression.getChildAt(0) as ts.CallExpression;
const expected = getTypes(expectedNode, typeChecker);

if (expected.diagnostic) {
return expected.diagnostic;
}

const argument = getTypes(node, typeChecker);

if (argument.diagnostic) {
return argument.diagnostic;
}

if (!typeChecker.isTypeSubtypeOf(argument.type, expected.type)) {
return typeError(
ErrorCode.ASSERT_TYPE_NOT_SUBTYPE,
typeChecker,
argument.type,
expected.type,
sourceFile,
expectedNode,
);
}

return;
}
5 changes: 5 additions & 0 deletions test/tssert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,8 @@ test('test-9', () => {
expectType('hello').identicalTo('hello');
expectType('hello').not.identicalTo('hello');
});

test('test-9', () => {
expectType<string>().subtypeOf('hello');
expectType<string>().not.subtypeOf('hello');
});

0 comments on commit ffbdfa6

Please sign in to comment.