Gdy obiekt ma identyfikator, który może być niezdefiniowany i chcesz wybrać wszystkie istniejące identyfikatory z tablicy tych obiektów, dlaczego następujące produkty wytwarzają błąd maszynowy mówiąc y.Id może być niezdefiniowany?

.filter(x => x.id !== undefined).map(y => y.id)
0
user3775501 19 październik 2020, 12:57

2 odpowiedzi

Najlepsza odpowiedź

Problem polega na tym, że .filter() nie zmienia automatycznie typu tablicy. Jeśli masz Array<T>, a następnie .filter() wytwarza Array<T>. W końcu nie jest łatwo określić co jesteś filtrowany. Rozważ to:

const input = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { name: "Carol" },
  { name: "David" },
];

const result = input.filter(x => x.name.includes("o"));

console.log(result);

Czy miałoby to prawo zmienić typ z Array<{id?: number, name: string}> do Array<{id: number, name: string}>? Nie, to nie. Filtr wyraźnie wytwarza elementy, które mogą nie mieć właściwości id. Jeśli nie znasz danych, to jeszcze trudniej określić, co robi filtrowanie i co to jest oznaczało do zrobienia.

Jednak maszynopis ma typowanie

interface Array<T> {
    filter<U extends T>(pred: (a: T) => a is U): U[];
}

Lub metoda .filter(), która przekształca się z Array<T> do Array<U>. Akceptuje Strażnik typu , co powinno powiedzieć, że filtrowanie wytwarza nowy typ. Co możesz zrobić, jest użyj strażnika typu, który przekonuje kompilator typu maszynopis, że następująca operacja masz identyfikatory. Należy pamiętać, że strażnicy typu muszą zwrócić boolean:

interface MyType {
  id?: number;
  name: string;
}

input
  .filter((x): x is MyType & {id: number} => "id" in x)
  .map(y => y.id);

Link do placu zabaw

Jednak nie będzie to złapać przypadków, w których właściwość id może być teraźniejszość , ale null lub undefined na przykład:

interface MyType {
  id?: number | null | undefined;
  name: string;
}

Więc musisz zmodyfikować logikę do (x): x is MyType & {id: number} => "id" in x && typeof x.id === "number". Zaczyna się być nieporęczny i nie jest łatwo wielokrotnego użytku, jeśli masz inny typ

interface Foo {
  id?: number | null | undefined;
  bar: string
}

Możesz uogólnić strażnik typu za pomocą generycznych, więc sprawdzi, że typ dowolny , który może mieć id:

type IdType<T extends {id?: any}> = T["id"]; //what is the type of the `id` property
type HasId<T> = T & {id: Exclude<IdType<T>, null | undefined>}; // T with a mandatory and non-null `id` property

function hasId<T extends { id?: any }>(item: T): item is HasId<T>{
  return "id" in item        // has `id`
    && item.id !== undefined // isn't `undefined`
    && item.id !== null;     // isn't `null`
}

Link do placu zabaw

Pozwoli Ci łatwo filtrować tablice, które zawierają typy z id łatwo:

const result: number[] = input
  .filter(hasId)
  .map(y => y.id);

Link do placu zabaw

0
VLAZ 19 październik 2020, 11:14

Możesz filtrować z niezdefiniowanego identyfikatora przez:

const a = [
  {id: 1},
  {something: 2},
  {id: 2},
]

console.log(a.filter(x => x.id).map(y => y.id))

// output: [1, 2]
-2
Mateusz Duma-Kurtas 19 październik 2020, 10:03