Why importing from typescript file messes up type checking

32 views Asked by At

I have a typescript file that just exports a constant openapi object schema:

export default {
  "title": "Draft",
  "description": "A new draft listing",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id"
  ],
  "properties": {
    "id": {
      "type": "string"
    }
  }
}

Then I am trying to add it to general open api schema (in another file) as a component:

import Draft from './__generated_schemas__/draft.js'

import { OpenAPIV3 } from 'openapi-types'

export const schema: OpenAPIV3.Document = {
  openapi: '3.1',
  info: {
    title: 'Properties API',
    version: '1.0.0',
    description:
      'Nice service description'
  },
  components: {
    schemas: {
      Draft
    }
  },
  paths: {}
}

If I do the above, I get TS complaining:

 packages/api/src/schema.ts(22,7): error TS2322: Type '{ title: string; description: string; type: string; additionalProperties: boolean; required: string[]; properties: { id: { type: string; }; }; }' is not assignable to type 'ReferenceObject | SchemaObject'.
   Type '{ title: string; description: string; type: string; additionalProperties: boolean; required: string[]; properties: { id: { type: string; }; }; }' is not assignable to type 'NonArraySchemaObject'.
     Types of property 'type' are incompatible.
       Type 'string' is not assignable to type 'NonArraySchemaObjectType | undefined'.

HOWEVER! Should I copy-paste the json from the TS file directly into the open-api schema, all works just fine:

export const schema: OpenAPIV3.Document = {
  openapi: '3.1',
  info: {
    title: 'Properties API',
    version: '1.0.0',
    description:
      'Nice service description'
  },
  components: {
    schemas: {
      Draft: {
        "title": "Draft",
        "description": "A new draft listing",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string"
          }
        }
      }
    }
  }

Any idea where TS type checking goes wrong here? I am 100% sure the object schema is correct, so is the overall open-api schema...

1

There are 1 answers

0
Stanislas On

I believe the key is in the last line: Type 'string' is not assignable to type 'NonArraySchemaObjectType | undefined'.

I don't know exactly what the NonArraySchemaObjectType is, you'd have to look at the typings for that, but I'm willing to bet that it is a union type of multiple constant strings. Something like: 'number' | 'string' | 'boolean'.

When you use a separate file that doesn't include any typings whatsoever, typescript will take a guess at each type.

For example, you pass:

export default {
  "title": "Draft",
  "description": "A new draft listing",
  "type": "object",
  ...
}

Which will result in a typing like:

{ 
  title: string, 
  desciprion: string, 
  type: string, 
  ...
}

BUT you don't want type to be just a normal string, you want the string to be a specific type: NonArraySchemaObjectType. Because there is no typing hint at all, to typescript this looks exactly the same as title or description.

The reason it does work in the same file, is because you specify at object creation: OpenAPIV3.Document.

Consider the following example:

let aString = "a"; // type is string
let aType = "a" as const; // type is "a"

type unionType = "a" | "b";

const ex1: { value: unionType } = {
    value: aType // ok, since "a" fits in "a" | "b"
};

const ex2: { value: unionType } = {
    value: "a" // ok, since typescript interprets that the "a" you typed here could be of type unionType
};

const ex3: { value: unionType } = {
    value: aString // Error, because you're assigning a string, which could be something else than "a" or "b"
};