If you wish to incorporate the compiler as an npm package, simply execute the command npm install typescript
. This enables you to utilize the compiler within your codebase. The following solution entails creating a small program to assess the compatibility of two types. While performance might be a concern, testing it with real-world scenarios will affirm its viability:
import * as ts from 'typescript'
// Enhance performance by caching declarations (such as lib.d.ts)
let sourceFileCache: { [name: string]: ts.SourceFile | undefined } = {};
function match(source: string, target: string): boolean {
let host = ts.createCompilerHost({});
let originalGetSourceFile = host.getSourceFile;
host.getSourceFile = (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => {
if (fileName === "compatCheck.ts") {
return ts.createSourceFile(fileName, `
type Source = ${source};
type Target = ${target};
let source!: Source;
let target!: Target;
target = source;
`, languageVersion);
}
if (sourceFileCache[fileName] === undefined) {
return sourceFileCache[fileName] = originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
} else {
return sourceFileCache[fileName];
}
}
let program = ts.createProgram(["compatCheck.ts"], {
}, host);
let errors = program.getSemanticDiagnostics();
return !errors.some(e => true);
}
console.log(match('number', 'any')); // true any can be assigned to number
console.log(match('any', 'number')); // true number can be assigned to any as well
console.log(match('{a: string; b: number}', '{a: string; b: number}')); // true
console.log(match('{a: string; b: number}', '{a: string}')); // true
console.log(match('{a: string}', '{a: string; b: number}')); // false
Edit
An alternative for enhanced performance involves excluding the parsing of default lib.d.ts
and supplying solely the essential types necessary for the compiler to function. This subset consists of Array
, Boolean
, Number
, Function
, IArguments
, Object
, RegExp
, and String
. Omitting methods in these types while including a basic definition ensures type incompatibility. Additional types may be added explicitly as required. For cases where method usage is necessary, inclusion of those specific methods is advised. However, considering the primary aim of comparing interfaces, this approach is unlikely to pose any issues:
import * as ts from "typescript";
// Cache declarations (e.g., lib.d.ts) to optimize performance
let sourceFileCache: { [name: string]: ts.SourceFile | undefined } = {};
function match(source: string, target: string): boolean {
let host = ts.createCompilerHost({});
let originalGetSourceFile = host.getSourceFile;
host.directoryExists = ()=> false;
host.fileExists = fileName => fileName === "compatCheck.ts";
host.getSourceFile = (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => {
if (fileName === "compatCheck.ts") {
return ts.createSourceFile(fileName, `
// Compiler Required Types
interface Array<T> { isArray: T & true }
type Boolean = { isBoolean: true }
type Function = { isFunction: true }
type IArguments = { isIArguments: true }
type Number = { isNumber: true }
type Object = { isObject: true }
type RegExp = { isRegExp: true }
type String = { isString: true }
type Source = ${source};
type Target = ${target};
let source!: Source;
let target!: Target;
target = source;
`, languageVersion);
}
if (sourceFileCache[fileName] === undefined) {
return sourceFileCache[fileName] = originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
} else {
return sourceFileCache[fileName];
}
}
let program = ts.createProgram(["compatCheck.ts"], {
noLib: true // Exclude default lib parsing, provide custom types
}, host);
let errors = program.getSemanticDiagnostics()
.concat(program.getDeclarationDiagnostics())
.concat(program.getConfigFileParsingDiagnostics())
.concat(program.getGlobalDiagnostics())
.concat(program.getOptionsDiagnostics())
.concat(program.getSyntacticDiagnostics());
return !errors.some(e => true);
}
console.log(match('number', 'any')); // true any can be assigned to number
console.log(match('any', 'number')); // true number can be assigned to any as well
console.log(match('{a: string; b: number}', '{a: string; b: number}')); // true
console.log(match('{a: string; b: number}', '{a: string}')); // true
console.log(match('{a: string}', '{a: string; b: number}')); // false