1

I am using graphql codegen together with urql to ensure back-to-back ts safety. But, after generating types and queries, I am having an issue typing the component props when passing data down to the component. Here is an example:

This is the query type generated:

export type AnnotationsQuery = { __typename?: 'Query', threads?: Array<{ __typename?: 'Thread', annotation: { __typename?: 'Annotation', id: string, x: number, y: number, width: number, height: number, key: number, color: string, stroke: number, style: string } }> | null };

Normally, when mapping over threads and passing thread as a prop to the component it could be typed as AnnotationsQuery["threads"][number] but in this case this will not work as the threads?: Array<> is optional, making AnnotationsQuery["threads"][number] not a proper type or any.

Is there a better/correct way to type a single thread even if the threads is optional?

Miloslavc
  • 127
  • 2
  • 7

2 Answers2

2

Typescript has a built-in utility type called NonNullable you can use to extract the type of AnnotationsQuery["threads"] when it is not undefined or null:

type Thread = NonNullable<AnnotationsQuery["threads"]>[number]

TS Playground

Jordan
  • 868
  • 1
  • 8
  • 25
0

You can use NonNullable


type ThreadOptional = NonNullable<AnnotationsQuery["threads"]>[number] | undefined;

interface Props {
  thread: ThreadOptional;
}

const ExampleComponent = ({ thread }: Props) => {
  // your component logic here
};

check it against simple unit test:

describe("ThreadOptional", () => {
  it("should correctly type a valid thread", () => {
    const thread: ThreadOptional = {
      __typename: "Thread",
      annotation: {
        __typename: "Annotation",
        id: "123",
        x: 0,
        y: 0,
        width: 10,
        height: 10,
        key: 1,
        color: "#000",
        stroke: 1,
        style: "solid"
      }
    };

    expect(thread).toBeDefined();
    expect(thread?.annotation.id).toBe("123");
  });

  it("should correctly type an undefined thread", () => {
    const thread: ThreadOptional = undefined;

    expect(thread).toBeUndefined();
  });

  it("should handle null or undefined AnnotationsQuery['threads']", () => {
    const query: AnnotationsQuery = { __typename: "Query", threads: null };

    const thread1: ThreadOptional = query.threads?.[0];
    expect(thread1).toBeUndefined();

    const thread2: ThreadOptional = undefined;
    expect(thread2).toBeUndefined();
  });
});
eudaimonia
  • 1,406
  • 1
  • 15
  • 28