Kontynuacja lekcji o naszym pluck. W sumie można powiedzieć, że całkiem nieźle już ogarniamy TypeScripta. Zatem do dzieła!

Takie przypomnienie:

function pluck1<T, K extends keyof T>(o: T, propertyNames: K[]): T[K][]{
    return propertyNames.map((n) => o[n] );
}

Nie jestem jakimś ekspertem w TS, ale mega nie podoba mi się typ zwracany. Dostać można oczopląsu, dlatego to poprawiliśmy:

function pluck1<T, K extends keyof T>(o: T, propertyNames: K[]): T[keyof T][]{
    return propertyNames.map((n) => o[n] );
}

const obj332 = {a:1, b: 2, c: 3};
const plucked = pluck1(obj332, ['a', 'b']); 
console.log(plucked) // [1,2]

Teraz wyjaśnienie:

  • keyof T – unia kluczy, jakie ma typ T (tu „a” | „b”, | „c”)
  • T[keyof T] – unia wartości, jakie mają klucze typu T (tu tylko number)
  • T[keyof T][] – to jest deklaracja tablicy, gdzie po lewej jej typ, po prawej znak, że to tablica (tutaj będzie number[] jako typ zwracany)

Czyli dodaj jeszcze pole ze stringiem, a T[keyof T][] będzie mogło zwracać (string|number)[]. Ufff….

Fajnie, fajnie, ale koleś ze stack overflow chce coś takiego:

const obj = {a:1, b: 2, c: 3};
const plucked = pluck(obj, 'a', 'b'); // {a: 1, b:2}

W dodatku nie wiem czy to typowe, ale naprawdę, żaden gość nie odpowiedział mu w pełni, każda z odpowiedzi zawierała jakiś feler, który nawet ja zauważam.

Dobra, po pierwsze, zwróćmy uwagę, on chce te argumenty przekazywać po przecinku. Już to w poprzedniej lekcji ogarnęliśmy:

function pluck1<T, K extends keyof T>(o: T, ...propertyNames: K[]): T[keyof T][]{
    return propertyNames.map((n) => o[n] );
}

const obj332 = {a:1, b: 2, c: 3};
const plucked = pluck1(obj332, 'a', 'b'); 
console.log(plucked) // [1,2]

Ok, teraz typ zwracany. Co będzie zwracać funkcja? Cóż, będzie zwracać obiekt, ale tylko z podanymi kluczami, czyli…

function pluck2<T, K extends keyof T>(o: T, ...propertyNames: K[]): Pick<T, K>{
    
}

Ok, pick pamiętamy. Teraz zobaczmy, co mu podpowiedzieli:

function pluck<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
    const ret: any = {};
    for (let key of keys) { 
        ret[key] = obj[key];
    }
    return ret; 
}

const less = pluck({ a: 1, b: '2', c: true }, ['a', 'c']);

On sobie tam w ret tał Pick<T,K>, podpowiedzieli mu, aby zostawił na any. Klucze też mu kazali jako tablica przekazywać. Pomyślmy…

function pluck2<T, K extends keyof T>(o: T, ...propertyNames: K[]): Pick<T, K>{
    const ret: Partial<T> = {};
}

Ok, idziemy do przodu… Dalej:

function pluck2<T, K extends keyof T>(o: T, ...propertyNames: K[]): Pick<T, K>{
    const ret: Partial<T> = {};
    for (let key of propertyNames) { 
        ret[key] = o[key];
    }
    return ret; 
}

Teraz mówią, że nie da się przypisać partiala<T> do pick<T,K>. Można:

function pluck2<T, K extends keyof T>(o: T, ...propertyNames: K[]): Pick<T, K>{
    const ret: Partial<T> = {};
    for (let key of propertyNames) { 
        ret[key] = o[key];
    }
    return ret as Pick<T,K>; 
}

Ok, zobaczmy czy działa:

function pluck2<T, K extends keyof T>(o: T, ...propertyNames: K[]): Pick<T, K>{
    const ret: Partial<T> = {};
    for (let key of propertyNames) { 
        ret[key] = o[key];
    }
    return ret as Pick<T,K>; 
}
const less = pluck2({ a: 1, b: '2', c: true }, 'a', 'c');

console.log(less); //{ a: 1, c: true }

Działa. Niby łatwe ćwiczenie, ale właśnie takie rozwiązując, uczymy się porządnie TSa.