>source

채팅 명령에 정적 제약을 도입하여 코드와 오류의 양을 줄이려고합니다. 명령은 이름과 인수로 설명됩니다. 내가 가진 것은 다음과 같습니다.

type ArgumentTypeName = 'string' | 'number' | 'user';
type CommandArguments<T> = { [k in keyof T]: ArgumentTypeName };
type CommandHandler<T> = (args: T) => void;
type Command<T> = {
  name: string;
  arguments: CommandArguments<T>;
  handler: CommandHandler<T>;
}

나는 얻는 방법을 알아낼 수 없다 T 입력 argumentsCommand<T> , 제대로 영향을 미칠 수 있도록 handler . 지금까지 올바른 키가있는 유형을 얻었지만 모든 값은 unknown . 이렇게 :

arguments = { arg1: 'string' }
// handler: (args: { arg1: unknown }) => void

나는 또한 제약을 시도했다 arguments , 내가 T를 지정하지 않으면 arguments ,하지만 작동하지 않았습니다.

type ArgumentName<T> = 
  T extends string ? 'string' :
  T extends number ? 'number' :
  T extends User ? 'user' :
  never;
type CommandArguments<T> = { [k in keyof T]: ArgumentName<T[k]> };
type CommandHandler<T> = (args: T) => void;
type Command<T> = {
  name: string;
  arguments: CommandArguments<T>;
  handler: CommandHandler<T>;
}

어떻게 내가 역 추론을 좋아할 수 있습니까? T 그 속성에 따라 CommandArguments<T> ?
그래서 나는 CommandArguments<T>TCommandHandler<T> ?
아니면 내가 생각하지 못한 다른 접근 방식이 있습니까?

편집하다: 이것을 사용하는 코드의 예 :

/*
In another file:
  const commands: Command<any>[] = [];
  export function createCommand<T>(cmd: Command<T>) {
    commands.push(cmd);
  }
*/
createCommand({
  name: 'add',
  arguments: {
    a: 'number',
    b: 'number'
  },
  handler: (args) => {
    // (parameter) args: { a: unknown; b: unknown; }
    // Error: Object is of type 'unknown'. ts(2571)
    console.log(args.a + args.b);
  },
});

  • 답변 # 1

    추론하기 CommandHandler ...에서 CommandArguments , 우리는 아마도 그 반대를 원하지 않을 것입니다. ArgumentName<T[k]>

    type CommandArguments<T> = { [k in keyof T]: ArgumentName<T[k]> };
    
    

    위의 접근 방식의 또 다른 문제점은 위 선언에서 CommandArguments 유일한 제약열쇠T , 그래서 T 의 속성유형은 단지 unknown .

    간단한 해결책은 제약 arguments 의 유형 T 객체를 확장하다 ArgumentTypeName , 추론 handler '에스 args 그 유형에서 T . 여기서 우리는 T extends CommandArguments 대신에 CommandArguments 실제 사용 유형을 캡처하기 위해.

    declare interface User{};
    type ArgumentType<T> =
      T extends 'string' ? string :
      T extends 'number' ? number :
      T extends 'user'   ? User   :
      never;
    type ArgumentTypeName = 'string' | 'number' | 'user';
    type CommandArguments = { [k: string]: ArgumentTypeName };
    type CommandHandler<T> = (args: { [k in keyof T]: ArgumentType<T[k]>}) => void;
    type Command<T extends CommandArguments> = {
      name: string;
      arguments: T;
      handler: CommandHandler<T>;
    }
    const commands: Command<any>[] = [];
    export function createCommand<T extends CommandArguments>(cmd: Command<T>) {
      commands.push(cmd);
    }
    createCommand({
      name: 'add',
      arguments: {
        a: 'number',
        b: 'string',
        c: 'user',
      },
      handler: (args) => {
        // (parameter) args: { a: number; b: string; c: User }
        console.log(args.a.toPrecision(2) + args.b.padStart(30));
      },
    });
    
    

    놀이터 링크

관련 자료

  • 이전 dart - Flutter에서 둥근 bottomAppbar를 구현하는 방법은 무엇입니까?
  • 다음 security - 성공적인 인증없이 UI5 앱 내부 페이지 표시 방지