Lists methods without TypeScript typings.
Usage:
mistypes <js> --- <dts>| # http://editorconfig.org/ | |
| root = true | |
| [*] | |
| indent_style = tab | |
| insert_final_newline = true | |
| trim_trailing_whitespace = true | |
| [*.md] | |
| indent_size = 3 |
| node_modules/ |
| #!/usr/bin/env ts-node-script -T | |
| import * as ts from "typescript"; | |
| import { tsIds } from "./ts-ids"; | |
| const files = process.argv.slice(2); | |
| interface InOut { | |
| minuend: string[]; | |
| subtrahend: string[]; | |
| } | |
| function inOut(files: readonly string[]): InOut { | |
| let idx = files.findIndex((s) => s === "---"); | |
| if (idx === -1) { | |
| idx = Infinity; | |
| } | |
| return { minuend: files.slice(0, idx), subtrahend: files.slice(idx + 1) }; | |
| } | |
| function* difference(minuend: Iterable<string>, subtrahend: Iterable<string>) { | |
| const set = new Set(subtrahend); | |
| for (const item of minuend) { | |
| if (!set.has(item)) { | |
| yield item; | |
| } | |
| } | |
| } | |
| function filesIds(files: readonly string[]) { | |
| const program = ts.createProgram(files, { allowJs: true }); | |
| return program.getSourceFiles().flatMap(tsIds); | |
| } | |
| const { minuend, subtrahend } = inOut(files); | |
| // It breaks without Array.from | |
| for (const entry of Array.from( | |
| difference(filesIds(minuend), filesIds(subtrahend)) | |
| )) { | |
| console.log(entry); | |
| } |
| { | |
| "name": "mistypes", | |
| "version": "0.0.0", | |
| "description": "Lists methods without TypeScript typings", | |
| "bin": "mistypes.ts", | |
| "dependencies": { | |
| "ts-node": "^8.8.2", | |
| "typescript": "^3.8.3" | |
| }, | |
| "devDependencies": { | |
| "@types/node": "^13.11.1", | |
| "prettier": "2.0.4" | |
| }, | |
| "repository": { | |
| "type": "git", | |
| "url": "https://gist.github.com/885dbe80be82e706e9e84c967269e17b.git" | |
| }, | |
| "keywords": [ | |
| "typescript" | |
| ], | |
| "author": "Wojciech Pawlik <[email protected]>", | |
| "license": "ISC" | |
| } |
| import * as ts from "typescript"; | |
| const isStatic = ({ kind }: ts.Modifier) => kind === 120; | |
| export function tsIds(file: ts.SourceFile): string[] { | |
| const ids: string[] = []; | |
| ts.forEachChild(file, (node) => { | |
| if (ts.isClassDeclaration(node)) { | |
| node.forEachChild((member) => { | |
| if (ts.isMethodDeclaration(member)) { | |
| const sep = member.modifiers?.some(isStatic) ? "." : "::"; | |
| ids.push( | |
| node.name?.escapedText + sep + member.name.getText(file) + "()" | |
| ); | |
| } | |
| }); | |
| } | |
| }); | |
| return ids; | |
| } |
| { | |
| "compilerOptions": { | |
| "module": "CommonJS", | |
| "noEmit": true, | |
| "strict": true, | |
| "target": "ES2019" | |
| } | |
| } |
Thanks @Ethan-Arrowood.
I wrote this just for telegraf/telegraf#979 and only tested it there. I stopped working on it as soon as it was usable from npx and I didn't notice any false negatives. I'm not experienced enough with TypeScript API to sustainably continue improving it.
Nice! I'll give this a shot with Fastify soon.
Related: tsdjs/tsd#63 (comment)
Wait this looks awesome. How extensively have you tested it?