typescript - Är det möjligt att säkerställa objektets fullständighet och behålla egenskapstyper?


Är det möjligt att skriva typen AllValidators, så att den säkerställer att ett objekt av dess typ har ALLA nycklar från ValidatorName OCH att varje egenskap behåller sin ursprungliga typ?

Så att konstanten validator längst ner förblir av typen Validator<string>.

(Jag vill inte att validator ska vara any, Validator<any>, Validator<string|number|boolean>)

Tack!

type ValidatorName = 'maxLength' | 'required' | 'regexp';

type Validator<P> = {
  (value: string, param: P): boolean;
};

type AllValidators = {
  [key in ValidatorName]: ???
}

const allValidators: AllValidators = {
  maxLength: ((value: string, param: number) => false) as Validator<number>,
  required: ((value: string, param: boolean) => false) as Validator<boolean>,
  regexp: ((value: string, param: string) => false) as Validator<string>,
}

const validator = allValidators.regexp

Svar #1

Du kan inte ha en variabel som både är begränsad till ett gränssnitt och få dess typ härledd baserat på det tilldelade värdet. Du kan istället använda en funktion med en typ-parameter. Typ-parametern kan ha en begränsning men den faktiska typen av parametern kommer att fångas från anropet.

type ValidatorName = 'maxLength' | 'required' | 'regexp';

type Validator<P> = {
    (value: string, param: P): boolean;
};

function createAllValidators<T extends { [P in ValidatorName]: Validator<any> }>(o: T) {
    return o;
}
const allValidators = createAllValidators({
    maxLength: ((value: string, param: number) => false),
    required: ((value: string, param: boolean) => false),
    regexp: ((value: string, param: string) => false),
})

const validator = allValidators.regexp

// { maxLength: number; required: boolean; regexp: string; }
type Rules = {
    [P in keyof typeof allValidators] : (typeof allValidators[P] extends Validator<infer T>? T: never)
}

Svar #2

Tja, det är ganska enkelt om du vänder på logiken och anger platser och använder typeof för att helt enkelt fånga typen av allValidators:

type Validator<P> = {
  (value: string, param: P): boolean;
};

const allValidators = {
  maxLength: ((value: string, param: number) => false) as Validator<number>,
  required: ((value: string, param: boolean) => false) as Validator<boolean>,
  regexp: ((value: string, param: string) => false) as Validator<string>,
}

type AllValidators = typeof allValidators;

const validator = allValidators.regexp

Du behöver inte ens ValidatorName längre (och det är diskutabelt om du överhuvudtaget behöver AllValidators också).