7

I have multiple types that form a larger, complex type that currently is used on my server. Is it possible to print the larger, complex type into console / file?

Example

type TypeA = {
  prop1: string;
  prop2: number;
}

type TypeB = Omit<TypeA, "prop2">;

console.logType(TypeB);
// {
//   prop1: string;
// }
5
  • Just console.log("value_you_want_to_see"); Commented Apr 23, 2021 at 1:37
  • 1
    @hoangdv That doesn't work for typescript types (ie type Something = Boolean) Commented Apr 23, 2021 at 3:45
  • 2
    Types don't exist at runtime. If you want to print out a type, you'll need to use the typescript compiler API, and then it will be very dependent on what you're trying to extract. Do you want the type of a value, the properties of an intersection, etc.? Can you give an example of what you're trying to print? Commented Apr 23, 2021 at 15:33
  • @chrisbajorin Updated with an example. I just need to export the Typescript files for use elsewhere. In my situation, the typescript types are generated by 3rd party library jsonschema-definer Commented Apr 23, 2021 at 17:10
  • Types (used by TypeScript) are Compile-Time. Values (used by console.log) are Run-Time. If you want something that covers both, you can use this trick: stackoverflow.com/a/59806829/2321594 Commented Dec 2, 2023 at 23:22

3 Answers 3

13

In order to extract the type signature, you'll need to use the compiler API. Assuming you have a file:

// ./src/my-file.ts
type TypeA = {
  prop1: string;
  prop2: number;
}

type TypeB = Omit<TypeA, "prop2">;

from a script outside your project root:

// ./type-printer.ts
import * as ts from "typescript";

function extractTypeSignature(filename: string, aliasName: string): string {

    const program: ts.Program = ts.createProgram([ filename ], { emitDeclarationOnly: true });
    const sourceFile: ts.SourceFile = program.getSourceFile(filename);
    const typeChecker: ts.TypeChecker = program.getTypeChecker();
    // Get the declaration node you're looking for by it's type name.
    // This condition can be adjusted to your needs
    const statement: ts.Statement | undefined = sourceFile.statements.find(
      (s) => ts.isTypeAliasDeclaration(s) && s.name.text === aliasName
    );
    if (!statement) {
        throw new Error(`Type: '${aliasName}' not found in file: '${filename}'`);
    }
    const type: ts.Type = typeChecker.getTypeAtLocation(statement);
    const fields: string[] = [];
    // Iterate over the `ts.Symbol`s representing Property Nodes of `ts.Type`
    for (const prop of type.getProperties()) {
        const name: string = prop.getName();
        const propType: ts.Type = typeChecker.getTypeOfSymbolAtLocation(prop, statement);
        const propTypeName: string = typeChecker.typeToString(propType);
        fields.push(`${name}: ${propTypeName};`);
    }
    return `type ${aliasName} = {\n  ${fields.join("\n  ")}\n}`;
}

const typeBSignature = extractTypeSignature("./src/my-file.ts", "TypeB");
// write to file or console log
console.log(typeBSignature);
/*
type TypeB = {
  prop1: string;
}
 */

I've explicitly annotated all variables to show where the types are coming from. Even though it's a small script, I'd recommend writing compiler scripts in TypeScript rather than JavaScript and executing with tsc file.ts && node file.js or using something like ts-node, as the type inference/typeguards are very useful when navigating the compiler API.

Sign up to request clarification or add additional context in comments.

4 Comments

If I wanted to get the type from a variable like const TypeC = ..., how would I? I tried changing it to isVariableDeclaration but the code complains that Property 'text' does not exist on type 'BindingName' for ...s.name.text....
How I usually navigate this is to log the kind on each node and then compare it to the SyntaxKind enum in typescript.d.ts. the kind on a const statement is 232, so I see it's a VariableStatement, and use the ts.isVariableStatement(s) typeguard, then dig into the available props. const a = 1,b = 2; is valid JS/TS syntax, so I noticed there is a declarationList on that node type, and each of those declarations has a name.getText().
Thanks for sharing this! This isn't getting deep types like type IMyPaymentEntities = NonNullable<UnwrapPromise<typeof fetchMyPaymentEntities>>;
+1. Thanks for this very interesting answer. It looks like it might be useful for my question. Do you think it could be adapted to find the resulting types (after inheritance) and then output an object of arrays like I'm asking here? stackoverflow.com/q/77588045/470749 Thanks.
0

Good IDEs, like WebStorm, will show you a new type:

Enter image description here

Enter image description here

8 Comments

I know I can see Typescript types in my IDE. My question is how to print them out into a file for use in other apps.
So if you want to use it in other apps then just use export type TypeB = Omit<TypeA, "prop2">; I think you will not find any better way.
The types are generated on the server. Its a combination of types meant only for the server with props Omitted for public API. I can't simply copy / export those types into my separate front end application.
If your types are "generated on the server" and you are not able to export them. Then how are you using it somewhere?
I'm using them on the server. But I wanted the public API types to be exported / printed to file so I can use in a separate web app. For example, I may want to give these files to a 3rd party so they can interact with my public apis. I don't want them knowing my internal / sensitive database types.
|
0

If you want to omit some types that you will be going to share with a third party then you will need to use @internal:

type First = {
  propA: string
  /** @internal */
  propB: number
}

And use stripInternal flag in the configuration when you will generate the .d.ts file.

{
  "include": ["aaa/**/*"],
  "compilerOptions": {
    "declaration": true,
    "emitDeclarationOnly": true,
    "outDir": "dist",
    "stripInternal": true
  }
}

3 Comments

Unfortunately, this option didn't work. It show generic outputs and included stuff that shouldn't be part of the file.
How is it possible? I tested the example that you described and it skipped that @internal types. Maybe you are doing something wrong?
I used the TypeB example found in my sample. It outputted something along the lines of type TypeB = Omit<TypeA, "prop2">; rather than type TypeB = { prop1: string; }. The other answer was able to extrapolate it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.