안녕하세요!

질문을 노션으로 정리해서 질문하는게 더 좋아 보여 노션을 이용하여 질문 드립니다!

사전 지식 (외부 API 타입)

// 외부 API, (node_modeuls/...)

declare type SelectPropertyResponse = {
    id: StringRequest;
    name: StringRequest;
    color: SelectColor;
};

// ...

export declare type GetPageResponse = {
    parent: {
        type: "database_id";
        database_id: IdRequest;
    } | {
        type: "page_id";
        page_id: IdRequest;
    } | {
        type: "workspace";
        workspace: true;
    };
    properties: Record<string, {
        type: "number";
        number: number | null;
        id: string;
    } | {
        type: "url";
        url: string | null;
        id: string;
    } | {
        type: "select";
        select: SelectPropertyResponse | null;
        id: string;
    } | {
        type: "multi_select";
        multi_select: Array<SelectPropertyResponse>;
        id: string;
    } | {
        type: "date";
        date: DateResponse | null;
        id: string;
    } | {
        type: "email";
        email: string | null;
        id: string;
    } | {
        type: "phone_number";
        phone_number: string | null;
        id: string;
    } | {
        type: "checkbox";
        checkbox: boolean;
        id: string;
    } | {
        type: "files";
        files: Array<{
            file: {
                url: string;
                expiry_time: string;
            };
            name: StringRequest;
            type?: "file";
        } | {
            external: {
                url: TextRequest;
            };
            name: StringRequest;
            type?: "external";
        }>;
        id: string;
    } | {
        type: "created_by";
        created_by: PartialUserObjectResponse;
        id: string;
    } | {
        type: "created_time";
        created_time: string;
        id: string;
    } | {
        type: "last_edited_by";
        last_edited_by: PartialUserObjectResponse;
        id: string;
    } | {
        type: "last_edited_time";
        last_edited_time: string;
        id: string;
    } | {
        type: "formula";
        formula: {
            type: "string";
            string: string | null;
        } | {
            type: "date";
            date: DateResponse | null;
        } | {
            type: "number";
            number: number | null;
        } | {
            type: "boolean";
            boolean: boolean | null;
        };
        id: string;
    } | {
        type: "title";
        title: Array<RichTextItemResponse>;
        id: string;
    } | {
        type: "rich_text";
        rich_text: Array<RichTextItemResponse>;
        id: string;
    } | {
        type: "people";
        people: Array<PartialUserObjectResponse>;
        id: string;
    } | {
        type: "relation";
        relation: Array<{
            id: string;
        }>;
        id: string;
    } | {
        type: "rollup";
        rollup: {
            type: "number";
            number: number | null;
            function: RollupFunction;
        } | {
            type: "date";
            date: DateResponse | null;
            function: RollupFunction;
        } | {
            type: "array";
            array: Array<{
                type: "title";
                title: Array<RichTextItemResponse>;
            } | {
                type: "rich_text";
                rich_text: Array<RichTextItemResponse>;
            } | {
                type: "people";
                people: Array<PartialUserObjectResponse>;
            } | {
                type: "relation";
                relation: Array<{
                    id: string;
                }>;
            }>;
            function: RollupFunction;
        };
        id: string;
    }>;
    icon: {
        type: "emoji";
        emoji: EmojiRequest;
    } | null | {
        type: "external";
        external: {
            url: TextRequest;
        };
    } | null | {
        type: "file";
        file: {
            url: string;
            expiry_time: string;
        };
    } | null;
    cover: {
        type: "external";
        external: {
            url: TextRequest;
        };
    } | null | {
        type: "file";
        file: {
            url: string;
            expiry_time: string;
        };
    } | null;
    created_by: {
        id: IdRequest;
        object: "user";
    };
    last_edited_by: {
        id: IdRequest;
        object: "user";
    };
    object: "page";
    id: string;
    created_time: string;
    last_edited_time: string;
    archived: boolean;
    url: string;
} | {
    object: "page";
    id: string;
};

타입 에러

Untitled

if ('properties' in postPage) {
  const properties = postPage['properties'];
  const _title = properties['title'];
  const _tags = properties['tags'];

  if (_title.type === 'title') {
    title = _title.title[0].plain_text;
  }
  if (_tags.type === 'multi_select') {
    tags = _tags.multi_select;
  }

  // ...
}

// 사용
const post: Post = {
  id: postPage.id,
  title,
  tags,
  description: postPage.properties.description['rich_text'][0]['plain_text'],
  createdTime: new Date(postPage.created_time).toLocaleDateString(),
};
  1. 여기서 궁금한게, postPage 도 union 타입 형태로 있어 타입 가드로 타입을 좁혔고, 이후 세부 속성도 title, tags, (description, createdTime 등도 설정해야함) 위 코드 처럼 하나하나 다 지정해야하는지 궁금합니다.

    ⇒ 해당 형태가 다른 곳에서도 사용하기 때문에 만약 필요하면, parse 하는 함수를 따로 빼야하나 궁금합니다.

  2. 외부 API 에 SelectPropertyResponse 이 declare 로 지정되어 있는데 해당 타입을 제 코드 상에서도 사용해야하는데 해당타입이 export 되어있지 않습니다.

    이럴 경우 그냥 제가 따로 타입을 선언해서 사용하는게 좋은지, 아니면 더 좋은 방법이 있는지 궁금합니다!

  3. 결국 GetPageResponse 의 properties 타입이 여러 union 타입으로 지정되어 있어 해당 타입을 타입가드로 하는 방법이 최선인지 궁금합니다

    다른 방법은 as 로 강제로 타입을 지정해야하는지 생각했는데.. 이런 경우 좋은 방법이 있는지 궁금합니다

    // 제가 구상한 방법??
    
    // 외부 API 에서 가져와야하는지, 외부 API 에서 참고해서 새로운 타입을 만들어야하는지...
    declare type SelectPropertyResponse = { // 정상 타입이라고 가정!
        id: StringRequest;
        name: StringRequest;
        color: SelectColor;
    };
    
    export type MultiSelectType = {
      type: 'multi_select';
      multi_select: {
        options: Array<SelectPropertyResponse>;
      };
      id: string;
      name: string;
    };
    
    // 사용하는 곳에서 
    const tags = (tagsDatabase.properties.tags as Multi_Select).multi_select.options;