typeshot
TypeScript icon, indicating that this package has built-in type declarations

3.1.0 • Public • Published

typeshot

npm version

typeshot is a general purpose code generator for TypeScript, especially focusing to generate type definitions without complex advanced types and TypeScript Compiler API.

Installation

$ npm i -D typeshot
$ npm i -D typescript prettier # You can skip them if already installed. 

Usage

Here is a sample code.

./usage-example.ts

import typeshot from 'typeshot';
import path from 'path';
 
typeshot.openTrace`
// DO NOT EDIT MANUALLY - GENERATED FILE
`;
 
export interface FileType {
  '.ts': object;
  '.tsx': string;
  '.png': Buffer;
  '.jpg': string;
}
 
typeshot.closeTrace();
 
const createFileInformationMap = typeshot.registerTypeDefinition((createTypeFragment, paths: string[]) => {
  const acc = Object.create(null);
 
  paths.forEach((p) => {
    const extname = path.extname(p) as keyof FileType;
    
    const fragment = createTypeFragment<{ [K in typeof p]: FileType[typeof extname] }>({ p, extname });
    
    Object.assign(acc, fragment);
  });
 
  return acc;
});
 
const paths: string[] = ['./foo/bar.ts', './baz/qux.png', './qux/baz.tsx', './bar/foo.jpg'];
 
typeshot.print`
// Hello, I'm comment.
export ${createFileInformationMap(paths).interface('FileInformationMap')}
`;

typeshot provides CLI command to evaluate source files. See CLI Options about CLI details.

typeshot --inputFile ./usage-example.ts --outDir ./results

./results/usage-example.ts

// DO NOT EDIT MANUALLY - GENERATED FILE
 
export interface FileType {
  '.ts': object;
  '.tsx': string;
  '.png': Buffer;
  '.jpg': string;
}
 
// Hello, I'm comment.
export interface FileInformationMap {
  './foo/bar.ts': object;
  './baz/qux.png': Buffer;
  './qux/baz.tsx': string;
  './bar/foo.jpg': string;
}

API

registerTypeDefinition

Example

const createFileInformationMap = typeshot.registerTypeDefinition((createTypeFragment, paths: string[]) => {
  const acc = Object.create(null);
 
  paths.forEach((p) => {
    const extname = path.extname(p) as keyof FileType;
    
    const fragment = createTypeFragment<{ [K in typeof p]: FileType[typeof extname] }>({ p, extname });
    
    Object.assign(acc, fragment);
  });
 
  return acc;
});

typeshot.registerTypeDefinition is a core API to create custom type definitions. It receives a function to describe type definition and returns a function to instantiate type definition. typeshot converts returned value of the describer function into type definitions by parsing the value down to TypeFragment and usual values and evaluating them. You can see how typeshot evaluates TypeFragment and values, by enabling a CLI Option -E/--emitIntermediateFiles.

The describer function receives a utility function called createTypeFragment and rest arguments which come from the instantiator function. There are two steps to instantiate type definitions, the first step is to pass arguments that the describer function requires, and the second step is to specify type format and type name.

In this example, it passes paths to describer function and specifies to output the instance as interface named FileInformationMap. Make sure to use typeshot.print to commit instances to the result.

typeshot.print`
// Hello, I'm comment.
export ${createFileInformationMap(paths).interface('FileInformationMap')}
`;

TypeFragment

TypeFragment is a special object which contains information to connect values and types.

Connecting values and types means that it's able to use actual values to certain parts in type annotations. Let's see an example.

const fragment = createTypeFragment<{ [K in typeof p]: FileType[typeof extname] }>({ p, extname });

createTypeFragment accepts one type argument and one argument. In the type argument, all type queries (typeof p and typeof extname) will be replaced with values received from argument. In this example, if p is './foo/bar.ts' and extname is .ts, then the type argument is internally evaluated as like this:

{ [K in './foo/bar.ts']FileType['.ts'] }

Note that the names of type query target must match with the keys of object at the argument.

Intersection Types

As the example does, you can merge TypeFragment into a one object. Merged object is evaluated as an intersection type. In this example, it's internally evaluated as like this:

& { [K in './foo/bar.ts']FileType['.ts'] }
& { [K in './baz/qux.png']FileType['.png'] }
& { [K in './qux/baz.tsx']FileType['.tsx'] }
& { [K in './bar/foo.jpg']FileType['.jpg'] }

General Values

registerTypeDefinition evaluates not only TypeFragment, but also normal values, into type definition.

Primitive values like string, number, boolean, undefined, and null are evaluated into literal type. Object-like values are evaluated recursively while keeping the structure of object.

Tuple Types

You can define tuple type by returning values via an array.

const example = typeshot.registerTypeDefinition(() => {
  return ['foo', 'bar', 'baz'];
});
 
typeshot.print`
type Example = ${example().literal()};
`;

This example will be evaluated into type Example = ['foo', 'bar', 'baz'];.

Union Types

You can define union types by using typeshot.union.

const example = typeshot.registerTypeDefinition(() => {
  return typeshot.union(['foo', 'bar', 'baz']);
});
 
typeshot.print`
type Example = ${example().literal()};
`;

This example will be evaluated into type Example = 'foo' | 'bar' | 'baz';.

print

typeshot.print is a tag function to commit type definition instance to result.

As the example does, it accepts extra contents to add comments, modifiers, etc.

typeshot.print`
// Hello, I'm comment.
export ${createFileInformationMap(paths).interface('FileInformationMap')}
`;

openTrace, closeTrace

You can copy and paste lines from source file to result file by using openTrace and closeTrace.

typeshot copies lines between openTrace and closeTrace, and paste to result file. You can also specify extra content by Tagged Template Literal.

typeshot.openTrace`
// DO NOT EDIT MANUALLY - GENERATED FILE
`;
 
export interface FileType {
  '.ts': object;
  '.tsx': string;
  '.png': Buffer;
  '.jpg': string;
}
 
typeshot.closeTrace();

CLI Options

typeshot [--outFile <path> | --outDir <path>] [--basePath <path>] [--rootDir <dir>] [--project <path>]
          [--prettierConfig <path>] [--systemModule <path>] [--maxParallel <count>] [--files]
          [--emitIntermediateFiles] [--inputFile <path> | [--] <path>...]
Flag(Shorthand) Default Descriptions
--inputFile(-i) - input file path
--outFile(-o) - output file path, make sure to use with --inputFile
--outDir(-O) - output directory path
--basePath(-b) process.cwd() base path to resolve relative paths
--rootDir basePath root directory to resolve output file path with outDir
--project(-p) 'tsconfig.json' tsconfig path
--prettierConfig - prettier config path
--systemModule 'typescript' see System Module
--maxParallel 3 max number of subprocess
--files false load all files in project if this flag is enabled, turn on this flag only if the result has some problem
--emitIntermediateFiles(-E) false whether to emit intermediate files
<rest arguments> - input file paths

System Module

typeshot supports injecting custom file system adapter by specifying --systemModule option. typeshot uses require('typescript').sys by default. But if you want to use custom behavior to pass custom file content, or to receive result files without emitting, you can specify path to your custom system module.

Make sure to export the implementation with name sys.

TODO

  • add missing tests
  • support async execution

Committers

License

Copyright 2020 Shota Hatada

Licensed under the MIT License.

Readme

Keywords

Package Sidebar

Install

npm i typeshot

Weekly Downloads

4

Version

3.1.0

License

MIT

Unpacked Size

90.3 kB

Total Files

63

Last publish

Collaborators

  • whatasoda