>웹 프론트엔드 >JS 튜토리얼 >고유 기호: 유형 안전을 위한 기호 사용 방법

고유 기호: 유형 안전을 위한 기호 사용 방법

Mary-Kate Olsen
Mary-Kate Olsen원래의
2025-01-16 10:48:09506검색

Unique Symbols: How to Use Symbols for Type Safety

React를 어느 정도 사용해 본 적이 있다면 React Query의 queryOptions() 함수를 우연히 접했을 것입니다. 구현은 놀라울 정도로 단순해 보입니다.

export function queryOptions(options: unknown) {
  return options
}

그러나 진짜 마법은 오버로드된 함수 서명에 있습니다. 그렇다면 무엇이 그렇게 특별한가요?

오버로드된 함수가 무엇인지 확실하지 않다면 다음 게시물을 확인하세요. 함수 오버로딩: 여러 함수 서명을 처리하는 방법

형식화된 데이터베이스 쿼리

React Query의 접근 방식에서 영감을 받아 React 외부에서 작업하는 사람들에게 유용할 수 있는 도우미 함수, 즉 SQL 쿼리와 같은 입력된 쿼리를 생성하는 간단한 방법을 구성했습니다.

export declare const queryParamsSymbol: unique symbol;
export declare const queryReturnSymbol: unique symbol;

export type Query<
  TParams extends Record<string, any> = Record<string, any>,
  TReturn extends Record<string, any> | undefined = undefined,
  TStatement extends string = string,
> = {
  statement: TStatement;
  [queryParamsSymbol]: TParams;
  [queryReturnSymbol]: TReturn;
};

export function query<
  TParams extends Record<string, any> = Record<string, any>,
  TReturn extends Record<string, any> | undefined = undefined,
  TStatement extends string = string,
>(statement: TStatement): Query<TParams, TReturn> {
  return { statement: statement } as Query<TParams, TReturn, TStatement>;
}

queryOptions()와 유사하게 함수 자체는 꽤 지루합니다. 즉, SQL 문을 가져와 이를 Query 유형의 객체로 래핑한 다음 반환합니다.

다음은 이를 어떻게 부르는지에 대한 간단한 예입니다.

const getUserById = query<{ id: number }, { name: string; email: string }>(
  'SELECT name, email FROM users WHERE id = $id',
);

두 가지 유형을 일반 매개변수로 전달하는 방법에 주목하세요. 첫 번째는 필수 쿼리 매개변수입니다. 이 경우에는 id입니다. 두 번째는 쿼리의 반환 유형(이름 및 이메일)을 나타냅니다.

내부적으로 query()는 이 두 가지 유형을 반환된 개체에 삽입하여 queryParamsSymbol 및 queryReturnSymbol에 숨겨둡니다. 이러한 기호는 고유 기호로 선언됩니다. 즉, 문자 공간에만 존재하고 트랜스파일된 JavaScript에는 표시되지 않습니다.

저는 이 기호를 사용하여 매개변수와 반환 유형을 임시로 저장하고 필요할 때마다 검색합니다.

type InferQueryParams<TQuery> = TQuery extends Query<infer Params, any> ? Params : never;

type UserQueryParams = InferQueryParams<typeof getUserById>;
//        ^? { id: number }

type InferQueryReturn<TQuery> = TQuery extends Query<any, infer Return> ? Return : never;

type UserQueryReturn = InferQueryReturn<typeof getUserById>;
//        ^? { name: string; email: string }

InferQueryParams 및 InferQueryReturn은 매개변수와 반환 유형이 올바르게 추론되고 있는지 확인하는 유틸리티 유형일 뿐입니다. 실제로는 필요하지 않을 수도 있지만 접근 방식을 확인하는 데 유용합니다.

데이터베이스 클라이언트

이제 쿼리 개체에 매개변수와 반환 유형을 포함하는 방법을 알았으니 실제로 이러한 쿼리를 어떻게 실행해야 할까요? 입력된 쿼리를 실행할 수 있는 간단한 데이터베이스 클라이언트를 살펴보겠습니다.

class DatabaseClient {
  async execute<
    TParams extends Record<string, any>, 
    TReturn extends Record<string, any>
  >(
    query: Query<TParams, TReturn>,
    params: TParams,
  ): Promise<Array<TReturn>> {
    // execute the query and return the results
    // ...
    return [];
  }
}

const db = new DatabaseClient();

// Return type and parameters are inferred from the query object
const result = await db.execute(getUserById, { id: 1 });
//                                              ^? { id: number }
//      ^? Array<{ name: string; email: string }>
console.log(result);

이 예에서는 입력된 getUserById 쿼리 객체를 db.execute() 메서드에 전달합니다. Query 유형에는 매개변수와 반환 유형 정보가 모두 포함되어 있으므로 TypeScript는 이를 자동으로 추론합니다. 결과 위로 마우스를 가져가면 이를 쉽게 확인할 수 있으며 TypeScript는 매개변수 객체의 속성으로 id를 제안합니다.

타입스크립트 플레이그라운드

이 TypeScript Playground에서 전체 코드 예제를 찾을 수 있습니다.

위 내용은 고유 기호: 유형 안전을 위한 기호 사용 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.