Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

typescript-is

Package Overview
Dependencies
Maintainers
1
Versions
91
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

typescript-is - npm Package Compare versions

Comparing version 0.1.12 to 0.2.0

.travis.yml

2

index.js
function is() {
throw new Error('This module should not be required in runtime. Instead, use a transformer during compilation.');
throw new Error('This module should not be used in runtime. Instead, use a transformer during compilation.');
}
module.exports = { is };
{
"name": "typescript-is",
"version": "0.1.12",
"version": "0.2.0",
"engines": {
"node": ">=6.14.4"
},
"description": "",
"main": "index.js",
"scripts": {
"build": "tsc"
"build": "rimraf lib/ && tsc --project tsconfig.json",
"lint": "tslint --project tsconfig.json",
"test": "ttsc --project tsconfig-test.json && mocha"
},

@@ -24,4 +29,10 @@ "repository": {

"devDependencies": {
"@types/node": ">=4"
"@types/mocha": "^5.2.5",
"@types/node": ">=4",
"mocha": "^5.2.0",
"rimraf": "^2.6.2",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"ttypescript": "^1.5.5"
}
}
import * as ts from 'typescript';
import * as path from 'path';
import { VisitorContext } from './visitor-context';
import { visitTypeNode } from './visitor';
import { transformNode } from './transform-node';

@@ -14,3 +13,4 @@ export interface Options {

checker: program.getTypeChecker(),
typeArgumentsStack: []
typeArgumentsStack: [],
typeMapperStack: []
};

@@ -25,20 +25,1 @@ return (context: ts.TransformationContext) => (file: ts.SourceFile) => transformNodeAndChildren(file, program, context, visitorContext);

}
function transformNode(node: ts.Node, visitorContext: VisitorContext): ts.Node {
if (ts.isCallExpression(node)) {
const signature = visitorContext.checker.getResolvedSignature(node);
if (
signature !== undefined
&& signature.declaration !== undefined
&& path.resolve(signature.declaration.getSourceFile().fileName) === path.resolve(path.join(__dirname, '..', '..', 'index.d.ts'))
&& node.arguments.length === 1
&& node.typeArguments !== undefined
&& node.typeArguments.length === 1
) {
const typeArgument = node.typeArguments[0];
const accessor = node.arguments[0];
return visitTypeNode(typeArgument, accessor, visitorContext);
}
}
return node;
}

@@ -7,2 +7,3 @@ import * as ts from 'typescript';

typeArgumentsStack: (ts.NodeArray<ts.TypeNode> | undefined)[];
typeMapperStack: ((source: ts.Type) => ts.Type | undefined)[];
}

@@ -5,115 +5,194 @@ import * as ts from 'typescript';

function reportNode(node: ts.Node) {
const sourceFile = node.getSourceFile();
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
return `${sourceFile.fileName}:${line + 1}:${character + 1}`;
function createPropertyCheck(accessor: ts.Expression, property: ts.Expression, type: ts.Type, optional: boolean, visitorContext: VisitorContext) {
const propertyAccessor = ts.createElementAccess(accessor, property);
const expression = visitType(type, propertyAccessor, visitorContext);
if (!optional) {
return expression;
} else {
return ts.createBinary(
ts.createLogicalNot(
ts.createBinary(
property,
ts.SyntaxKind.InKeyword,
accessor
)
),
ts.SyntaxKind.BarBarToken,
expression
);
}
}
function stringifyTypeNodes(nodes: ts.NodeArray<ts.TypeNode> | undefined, visitorContext: VisitorContext): string {
return nodes === undefined ? '' : '<' + nodes.map((node) => stringifyTypeNode(node, visitorContext)).join(',') + '>';
}
function stringifyTypeNode(node: ts.TypeNode, visitorContext: VisitorContext): string {
if (node.kind === ts.SyntaxKind.NumberKeyword) {
return 'number';
} else if (node.kind === ts.SyntaxKind.StringKeyword) {
return 'string';
} else if (node.kind === ts.SyntaxKind.BooleanKeyword) {
return 'boolean';
} else if (ts.isTypeReferenceNode(node)) {
const type = visitorContext.checker.getTypeFromTypeNode(node);
const fqn = visitorContext.checker.getFullyQualifiedName(type.symbol);
const typeArgumentsPostfix = stringifyTypeNodes(node.typeArguments, visitorContext);
return fqn + typeArgumentsPostfix;
} else if (ts.isLiteralTypeNode(node)) {
if (ts.isStringLiteral(node.literal)) {
return JSON.stringify(node.literal.text);
} else {
throw new Error('Unsupported LiteralTypeNode kind: ' + node.kind);
}
function visitPropertyName(node: ts.PropertyName, accessor: ts.Expression, visitorContext: VisitorContext) {
// Identifier | StringLiteral | NumericLiteral | ComputedPropertyName
if (ts.isIdentifier(node)) {
return ts.createStringLiteral(node.text);
} else if (ts.isStringLiteral(node)) {
return ts.createStringLiteral(node.text);
} else if (ts.isNumericLiteral(node)) {
return ts.createStringLiteral(node.text);
} else {
throw new Error('Unsupported TypeNode kind: ' + node.kind);
return node.expression;
}
}
function visitNumberKeyword(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) {
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('number'));
function visitPropertySignature(node: ts.PropertySignature, accessor: ts.Expression, visitorContext: VisitorContext) {
if (node.type === undefined) {
throw new Error('Visiting property without type.');
}
const type = visitorContext.checker.getTypeFromTypeNode(node.type);
return createPropertyCheck(accessor, visitPropertyName(node.name, accessor, visitorContext), type, node.questionToken !== undefined, visitorContext);
}
function visitBooleanKeyword(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) {
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('boolean'));
function visitDeclaration(node: ts.Declaration, accessor: ts.Expression, visitorContext: VisitorContext) {
if (ts.isPropertySignature(node)) {
return visitPropertySignature(node, accessor, visitorContext);
} else {
throw new Error('Unsupported declaration kind: ' + node.kind);
}
}
function visitStringKeyword(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) {
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('string'));
function visitArrayObjectType(type: ts.ObjectType, accessor: ts.Expression, visitorContext: VisitorContext) {
const numberIndexType = visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.Number);
if (numberIndexType === undefined) {
throw new Error('Expected array ObjectType to have a number index type.');
}
const itemIdentifier = ts.createIdentifier('item');
return ts.createBinary(
ts.createCall(
ts.createPropertyAccess(ts.createIdentifier('Array'), ts.createIdentifier('isArray')),
undefined,
[accessor]
),
ts.SyntaxKind.AmpersandAmpersandToken,
ts.createCall(
ts.createPropertyAccess(accessor, ts.createIdentifier('every')),
undefined,
[
ts.createArrowFunction(
undefined,
undefined,
[
ts.createParameter(
undefined,
undefined,
undefined,
itemIdentifier
)
],
undefined,
undefined,
ts.createBlock([
ts.createReturn(visitType(numberIndexType, itemIdentifier, visitorContext))
])
)
]
)
);
}
function visitInterfaceDeclaration(node: ts.InterfaceDeclaration, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression {
const typeArguments = visitorContext.typeArgumentsStack[visitorContext.typeArgumentsStack.length - 1];
const conditions: ts.Expression[] = [];
conditions.push(ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('object')));
conditions.push(ts.createStrictInequality(accessor, ts.createNull()));
const typeParameterTypes = node.typeParameters === undefined
? undefined
: node.typeParameters.map((typeParameter) => visitorContext.checker.getTypeAtLocation(typeParameter));
if (node.heritageClauses) {
for (const heritageClause of node.heritageClauses) {
for (const heritageType of heritageClause.types) {
if (ts.isIdentifier(heritageType.expression)) {
const heritageTypeArguments = heritageType.typeArguments === undefined
? undefined
: heritageType.typeArguments.map((typeArgument) => {
const type = visitorContext.checker.getTypeFromTypeNode(typeArgument);
let typeNode = typeArgument;
if (typeArguments !== undefined && typeParameterTypes !== undefined) {
const index = typeParameterTypes.findIndex((typeParameterType) => typeParameterType === type);
if (index >= 0) {
typeNode = typeArguments[index];
}
function visitRegularObjectType(type: ts.ObjectType, accessor: ts.Expression, visitorContext: VisitorContext) {
const mappers: ((source: ts.Type) => ts.Type | undefined)[] = [];
(function checkBaseTypes(type: ts.Type) {
if (tsutils.isTypeReference(type) && tsutils.isInterfaceType(type.target)) {
const baseTypes = visitorContext.checker.getBaseTypes(type.target);
for (const baseType of baseTypes) {
if (tsutils.isTypeReference(baseType) && baseType.target.typeParameters !== undefined && baseType.typeArguments !== undefined) {
const typeParameters = baseType.target.typeParameters;
const typeArguments = baseType.typeArguments;
mappers.push((source: ts.Type) => {
for (let i = 0; i < typeParameters.length; i++) {
if (source === typeParameters[i]) {
return typeArguments[i];
}
return typeNode;
});
const type = visitorContext.checker.getTypeAtLocation(heritageType.expression);
visitorContext.typeArgumentsStack.push(ts.createNodeArray(heritageTypeArguments));
const expression = type.symbol.declarations
.map((declaration) => visitDeclaration(declaration, accessor, visitorContext))
.reduce((condition, expression) =>
ts.createBinary(
condition,
ts.SyntaxKind.AmpersandAmpersandToken,
expression
)
);
conditions.push(expression);
visitorContext.typeArgumentsStack.pop();
} else {
throw new Error('Expected heritage type expression to be an identifier.');
}
});
checkBaseTypes(baseType);
}
}
}
}
for (const member of node.members) {
if (ts.isPropertySignature(member)) {
const memberAccessor = ts.createPropertyAccess(accessor, tsutils.getPropertyName(member.name));
if (member.type !== undefined) {
let typeNode = member.type;
if (typeArguments !== undefined) {
const type = visitorContext.checker.getTypeFromTypeNode(member.type);
let matchedTypeArgumentIndex = -1;
if (typeParameterTypes !== undefined) {
matchedTypeArgumentIndex = typeParameterTypes.findIndex((typeParameterType) => typeParameterType === type);
})(type);
if (tsutils.isTypeReference(type)) {
if (type.target.typeParameters !== undefined && type.typeArguments !== undefined) {
const typeParameters = type.target.typeParameters;
const typeArguments = type.typeArguments;
mappers.push((source: ts.Type) => {
for (let i = 0; i < typeParameters.length; i++) {
if (source === typeParameters[i]) {
return typeArguments[i];
}
if (matchedTypeArgumentIndex >= 0) {
typeNode = typeArguments[matchedTypeArgumentIndex];
}
}
// TODO: member optional
conditions.push(visitTypeNode(typeNode, memberAccessor, visitorContext));
});
}
}
const mapper = mappers.reduce<(source: ts.Type) => ts.Type | undefined>((previous, next) => (source: ts.Type) => previous(source) || next(source), () => undefined);
const conditions: ts.Expression[] = [
ts.createStrictEquality(
ts.createTypeOf(accessor),
ts.createStringLiteral('object')
),
ts.createStrictInequality(
accessor,
ts.createNull()
),
ts.createLogicalNot(
ts.createCall(
ts.createPropertyAccess(ts.createIdentifier('Array'), ts.createIdentifier('isArray')),
undefined,
[accessor]
)
)
];
visitorContext.typeMapperStack.push(mapper);
for (const property of visitorContext.checker.getPropertiesOfType(type)) {
if ('valueDeclaration' in property) {
conditions.push(visitDeclaration(property.valueDeclaration, accessor, visitorContext));
} else {
// Using internal TypeScript API, hacky.
const propertyType = (property as { type?: ts.Type }).type;
const propertyName = (property as { name?: string }).name;
const optional = ((property as ts.Symbol).flags & ts.SymbolFlags.Optional) !== 0;
if (propertyType !== undefined && propertyName !== undefined) {
conditions.push(createPropertyCheck(accessor, ts.createStringLiteral(propertyName), propertyType, optional, visitorContext));
}
}
}
const stringIndexType = visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.String);
if (stringIndexType) {
const keyIdentifier = ts.createIdentifier('key');
const itemAccessor = ts.createElementAccess(accessor, keyIdentifier);
conditions.push(
ts.createCall(
ts.createPropertyAccess(
ts.createCall(
ts.createPropertyAccess(ts.createIdentifier('Object'), ts.createIdentifier('keys')),
undefined,
[accessor]
),
ts.createIdentifier('every')
),
undefined,
[
ts.createArrowFunction(
undefined,
undefined,
[
ts.createParameter(
undefined,
undefined,
undefined,
keyIdentifier
)
],
undefined,
undefined,
ts.createBlock([
ts.createReturn(visitType(stringIndexType, itemAccessor, visitorContext))
])
)
]
)
);
}
visitorContext.typeMapperStack.pop();
return conditions.reduce((condition, expression) =>

@@ -128,62 +207,90 @@ ts.createBinary(

function visitDeclaration(node: ts.Declaration, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression {
let expression: ts.Expression;
if (ts.isInterfaceDeclaration(node)) {
expression = visitInterfaceDeclaration(node, accessor, visitorContext);
} else if (ts.isTypeParameterDeclaration(node)) {
throw new Error('Unbound type parameter: ' + node.getText() + ' at ' + reportNode(node));
function visitObjectType(type: ts.ObjectType, accessor: ts.Expression, visitorContext: VisitorContext) {
if (visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.Number)) {
return visitArrayObjectType(type, accessor, visitorContext);
} else {
throw new Error('Unsupported declaration kind: ' + node.kind);
return visitRegularObjectType(type, accessor, visitorContext);
}
return expression;
}
function visitTypeReferenceNode(node: ts.TypeReferenceNode, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression {
const type = visitorContext.checker.getTypeFromTypeNode(node);
visitorContext.typeArgumentsStack.push(node.typeArguments);
const expression = type.symbol.declarations
.map((declaration) => visitDeclaration(declaration, accessor, visitorContext))
.reduce((condition, expression) => ts.createBinary(condition, ts.SyntaxKind.AmpersandAmpersandToken, expression));
visitorContext.typeArgumentsStack.pop();
return expression;
function visitLiteralType(type: ts.LiteralType, accessor: ts.Expression, visitorContext: VisitorContext) {
if (typeof type.value === 'string') {
return ts.createStrictEquality(accessor, ts.createStringLiteral(type.value));
} else if (typeof type.value === 'number') {
return ts.createStrictEquality(accessor, ts.createNumericLiteral(type.value.toString()));
} else {
throw new Error('Type value is expected to be a string or number.');
}
}
function visitLiteralTypeNode(node: ts.LiteralTypeNode, accessor: ts.Expression, visitorContext: VisitorContext) {
if (ts.isStringLiteral(node.literal)) {
return ts.createStrictEquality(accessor, ts.createStringLiteral(node.literal.text));
function visitUnionOrIntersectionType(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext) {
let token: ts.SyntaxKind.BarBarToken | ts.SyntaxKind.AmpersandAmpersandToken;
if (tsutils.isUnionType(type)) {
token = ts.SyntaxKind.BarBarToken;
} else if (tsutils.isIntersectionType(type)) {
token = ts.SyntaxKind.AmpersandAmpersandToken;
} else {
throw new Error('Unsupported LiteralTypeNode kind: ' + node.kind);
throw new Error('UnionOrIntersection type is expected to be a Union or Intersection type.');
}
return type.types
.map((type) => visitType(type, accessor, visitorContext))
.reduce((condition, expression) => ts.createBinary(condition, token, expression));
}
export function visitTypeNode(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) {
/* if (node.kind === ts.SyntaxKind.AnyKeyword) {
name = 'any';
} else if (node.kind === ts.SyntaxKind.UnknownKeyword) {
name = 'unknown';
} else*/
if (node.kind === ts.SyntaxKind.NumberKeyword) {
return visitNumberKeyword(node, accessor, visitorContext);
/*} else if (node.kind === ts.SyntaxKind.ObjectKeyword) {
name = 'object';*/
} else if (node.kind === ts.SyntaxKind.BooleanKeyword) {
return visitBooleanKeyword(node, accessor, visitorContext);
} else if (node.kind === ts.SyntaxKind.StringKeyword) {
return visitStringKeyword(node, accessor, visitorContext);
/*} else if (node.kind === ts.SyntaxKind.VoidKeyword) {
name = 'void';
} else if (node.kind === ts.SyntaxKind.UndefinedKeyword) {
name = 'undefined';
} else if (node.kind === ts.SyntaxKind.NullKeyword) {
name = 'null';
} else if (node.kind === ts.SyntaxKind.NeverKeyword) {
name = 'never';
}*/
} else if (ts.isTypeReferenceNode(node)) {
return visitTypeReferenceNode(node, accessor, visitorContext);
} else if (ts.isLiteralTypeNode(node)) {
return visitLiteralTypeNode(node, accessor, visitorContext);
function visitBooleanLiteral(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext) {
// Using internal TypeScript API, hacky.
return ts.createStrictEquality(
accessor,
(type as { intrinsicName?: string }).intrinsicName === 'true'
? ts.createTrue()
: ts.createFalse()
);
}
function visitTypeParameter(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext) {
const typeMapper = visitorContext.typeMapperStack[visitorContext.typeMapperStack.length - 1];
if (typeMapper === undefined) {
throw new Error('Unbound type parameter, missing type mapper.');
}
const mappedType = typeMapper(type);
if (mappedType === undefined) {
throw new Error('Unbound type parameter, missing type node.');
}
return visitType(mappedType, accessor, visitorContext);
}
export function visitType(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression {
if ((ts.TypeFlags.Any & type.flags) !== 0) {
// Any -> always true
return ts.createTrue();
} else if ((ts.TypeFlags.Number & type.flags) !== 0) {
// Number
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('number'));
} else if ((ts.TypeFlags.Boolean & type.flags) !== 0) {
// Boolean
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('boolean'));
} else if ((ts.TypeFlags.String & type.flags) !== 0) {
// String
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('string'));
} else if ((ts.TypeFlags.BooleanLiteral & type.flags) !== 0) {
// Boolean literal (true/false)
return visitBooleanLiteral(type, accessor, visitorContext);
} else if ((ts.TypeFlags.TypeParameter & type.flags) !== 0) {
// Type parameter
return visitTypeParameter(type, accessor, visitorContext);
} else if (tsutils.isObjectType(type)) {
// Object type (including arrays)
return visitObjectType(type, accessor, visitorContext);
} else if (tsutils.isLiteralType(type)) {
// Literal string/number types ('foo')
return visitLiteralType(type, accessor, visitorContext);
} else if (tsutils.isUnionOrIntersectionType(type)) {
// Union or intersection type (using | or &)
return visitUnionOrIntersectionType(type, accessor, visitorContext);
} else if ((ts.TypeFlags.Never & type.flags) !== 0) {
// Never -> always false
return ts.createFalse();
} else {
throw new Error('Unsupported TypeNode kind: ' + node.kind);
throw new Error('Unsupported type with flags: ' + type.flags);
}
}

@@ -40,3 +40,3 @@ import * as ts from 'typescript';

);
const newSourceFile = ts.updateSourceFileNode(sourceFile, compile(visitorContext))
const newSourceFile = ts.updateSourceFileNode(sourceFile, compile(visitorContext));
const printer = ts.createPrinter({

@@ -43,0 +43,0 @@ newLine: ts.NewLineKind.LineFeed

{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "lib"
"target": "es6",
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noUnusedLocals": true,
"strict": true,
"sourceMap": true,
"declaration": true,
"outDir": "test"
},

@@ -6,0 +15,0 @@ "include": [

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc