Obținerea tipului unei proprietăți a unei clase TypeScript folosind operatorul keyof


Așa cum este menționat în documentația TypeScript despre operatorul keyof, se poate obține o proprietate a unei instanțe de obiect folosind funcția de mai jos.

function getProperty<T, K extends keyof T>(o: T, name: K) {
    return o[name];
}

Desigur, se poate obține tipul proprietății înlocuind return o[name] cu return typeof o[name]. Există vreo metodă de a obține tipul proprietății fără a transmite vreo instanță de obiect?

function getPropertyType<T>(name: keyof T) {
    // something like T[name]?
}

Răspuns #1

Da, tipurile de căutare funcționează foarte bine:

type BarType = FooType['bar'];

Se așteaptă în acest caz ca FooType să fie un obiect de genul:

type FooType = {
    bar: string;
}

Setează BarType la același tip ca FooType['bar'], deci la un string.

PS: FooType poate fi, de asemenea, o interfață sau o clasă.

Răspunsul #2

Este asta ceea ce cauți?

type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];

și obține tipul unei proprietăți a unui obiect făcând:

type MyPropType = PropType<ObjType, '<key>'>;

ceea ce este la fel ca modul de utilizare al Pick în TypeScript și poate raporta eroare de compilare dacă este transmisă vreo cheie invalidă.

Actualizări

Așa cum a sugerat @astoilkov, o alternativă mai simplă este PropType['key'].

Răspunsul #3

Desigur, se poate obține tipul proprietății înlocuind return o[name] cu return typeof o[name].

Nu chiar... dacă ai face asta:

function getTypeofProperty<T, K extends keyof T>(o: T, name: K) {
    return typeof o[name];
}

Ai obține o valoare de returnare de tip "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function", deoarece folosești operatorul JavaScript typeof la rulat, care returnează un șir de caractere precum "object", nu tipul la compilare văzut de TypeScript.

TypeScript folosește de asemenea cuvântul cheie typeof ca operator de interogare a tipului la timpul de compilare. Poți să observi diferența uitându-te la locurile unde apare. Interogarea de tip typeof apare numai în locurile unde scrii un tip. De exemplu:

const a = { foo: 0 };
const b: Array<typeof a> = [{ foo: 1 }, { foo: 2 }, {foo: 3}]; 
const c: string = typeof a; // "object"

În b, poți vedea că typeof a apare acolo unde ai scrie o expresie de tip, deci este o interogare de tip TypeScript, iar Array<typeof a> este evaluat ca Array<{foo: number}>. Pe de altă parte, în c, typeof a apare acolo unde ai scrie o expresie de valoare, deci este operatorul JavaScript typeof și va fi evaluat la șirul "object" la rulare.

Așa cum a menționat @k0pernikus, TypeScript nu permite (și nu are intenția să permită) să obții informații despre tip la timpul compilării în timpul execuției. Deci, nu există un operator typeof care să acționeze la timpul execuției și să returneze informații despre tip la timpul compilării.

Desigur, dacă vrei să obții informații despre tipul unei proprietăți în timpul compilării, poți face asta, folosind ceea ce se numește tipuri de căutare. Să examinăm valoarea returnată a funcției getProperty():

function getProperty<T, K extends keyof T>(o: T, name: K) {
    return  o[name];
} // hover over getProperty to see that the return value is of type T[K]

Acest T[K] într-o poziție de tip înseamnă „tipul proprietății cu o cheie de tip K pe un obiect de tip T”. Deoarece aceasta este o operațiune la nivel de tip, o puteți face în timpul compilării fără a declara nicio valoare de tip T sau K. De exemplu,

type RegExpFlags = RegExp['flags']; // RegExpFlags is string
const flags: RegExpFlags = 'ig';

Aici, cauți cheia "flags" a tipului de obiecte RegExp și obții înapoi tipul string, și poți declara o valoare de tip RegExp['flags'] fără a avea nicăieri o valoare de tip RegExp.

Asta este cea mai apropiată răspuns pe care o pot oferi la întrebarea ta fără mai multe informații despre ceea ce ai nevoie. Sper că ajută. Mult noroc!