как удалить свойства и метод обещания через сопоставленный тип в TypeScript

Вот код

class A {
    x = 0;
    y = 0;
    visible = false;
    render() {
        return 1;
    }
}

type RemoveProperties<T> = {
    readonly [P in keyof T]: T[P] extends Function ? T[P] : never//;
};

type JustMethodKeys<T> = ({ [P in keyof T]: T[P] extends Function ? P : never })[keyof T];
type JustMethods<T> = Pick<T, JustMethodKeys<T>>;


type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;

type Promisified<T extends Function> =
    T extends (...args: any[]) => Promise<any> ? T : (
        T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
            IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => Promise<R> :
            IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => Promise<R> :
            IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => Promise<R> :
            IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => Promise<R> :
            IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => Promise<R> :
            IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => Promise<R> :
            IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => Promise<R> :
            IsValidArg<C> extends true ? (a: A, b: B, c: C) => Promise<R> :
            IsValidArg<B> extends true ? (a: A, b: B) => Promise<R> :
            IsValidArg<A> extends true ? (a: A) => Promise<R> :
            () => Promise<R>
        ) : never
    );



var a = new A() as JustMethods<A>  // I want to JustMethod && Promisified
a.visible // error
var b = a.render() // b should be Promise<number>

Как это реализовать? Я хочу удалить метод видимого и обещанного рендеринга, как скомпоновать Promisified и JustMethods?

Как это реализовать? Я хочу удалить метод видимого и обещанного рендеринга, как скомпоновать Promisified и JustMethods?


person Wander Wang    schedule 22.03.2018    source источник


Ответы (2)


Вам нужно использовать сопоставленный тип, который принимает только методы типа, использующие JustMethodKeys, и использует Promisified для каждого свойства.

class A {
    x = 0;
    y = 0;
    visible = false;
    render() {
        return 1;
    }
}

type JustMethodKeys<T> = ({ [P in keyof T]: T[P] extends Function ? P : never })[keyof T];

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;

type Promisified<T extends Function> =
    T extends (...args: any[]) => Promise<any> ? T : (
        T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
            IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => Promise<R> :
            IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => Promise<R> :
            IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => Promise<R> :
            IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => Promise<R> :
            IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => Promise<R> :
            IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => Promise<R> :
            IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => Promise<R> :
            IsValidArg<C> extends true ? (a: A, b: B, c: C) => Promise<R> :
            IsValidArg<B> extends true ? (a: A, b: B) => Promise<R> :
            IsValidArg<A> extends true ? (a: A) => Promise<R> :
            () => Promise<R>
        ) : never
    );

type PromisifyMethods<T> = { 
    // We take just the method key and Promisify them, 
    // We have to use T[P] & Function because the compiler will not realize T[P] will always be a function
    [P in JustMethodKeys<T>] : Promisified<T[P] & Function>
}

//Usage
declare var a : PromisifyMethods<A>  
a.visible // error
var b = a.render() // b is Promise<number>

Изменить

Поскольку на исходный вопрос был дан ответ, машинописный текст улучшил возможное решение этой проблемы. С добавлением кортежей в остальных параметрах и выражениях распространения нам больше не нужно иметь все перегрузки для Promisified:

type JustMethodKeys<T> = ({ [P in keyof T]: T[P] extends Function ? P : never })[keyof T];


type ArgumentTypes<T> = T extends (... args: infer U ) => any ? U: never;
type Promisified<T> = T extends (...args: any[])=> infer R ? (...a: ArgumentTypes<T>) => Promise<R> : never;

type PromisifyMethods<T> = { 
    // We take just the method key and Promisify them, 
    // We have to use T[P] & Function because the compiler will not realize T[P] will always be a function
    [P in JustMethodKeys<T>] : Promisified<T[P]>
}

//Usage
declare var a : PromisifyMethods<A>  
a.visible // error
var b = a.render("") // b is Promise<number> , render is render: (k: string) => Promise<number>

Это не только короче, но и решает ряд проблем

  • Необязательные параметры остаются необязательными
  • Имена аргументов сохраняются
  • Работает для любого количества аргументов
person Titian Cernicova-Dragomir    schedule 22.03.2018
comment
@Florian Я не думаю, что это то, что ты ищешь. Похоже, вам нужны декораторы. Этот вопрос строго связан с изменением сигнатур функций в классе, он не имеет поведения во время выполнения. Но задайте вопрос, и кто-то ответит на него :-) - person Titian Cernicova-Dragomir; 12.09.2018

Похоже на другой ответ, но вот к чему я пришел

type Method = (...args: any) => any;
type KeysOfMethods<T> = ({ [P in keyof T]: T[P] extends Method ? P : never })[keyof T];
type PickMethods<T> = Pick<T, KeysOfMethods<T>>;
type Promisify<T> = T extends Promise<infer U> ? Promise<U> : Promise<T>;
type PromisifyMethod<T extends Method> = (...args: Parameters<T>) => Promisify<ReturnType<T>>;
type PromisifyMethods<T> = { [P in keyof T]: T[P] extends Method ? PromisifyMethod<T[P]> : T[P] };

type PromisifiedMethodsOnly<T> = PickMethods<PromisifyMethods<T>>
person Jan    schedule 26.08.2019