Búsqueda de sitios web

typescript - ¿Cómo puedo acceder a una propiedad común de tipos de objetos anidados que a su vez se acceden con parámetros de tipo genérico?


A continuación hay un tipo genérico simple, "GetA", que se supone que simplemente indexa un objeto de tipo para obtener un tipo anidado; utiliza parámetros genéricos como dos de los índices. Sin embargo, en el último índice no genérico, lanza un error de tipo ("a" no se puede usar para indexar...), aunque todos los objetos posibles tengan la clave indexada. Espero que alguien pueda ayudarme a comprenderlo.

¿Cómo puedo hacer una versión funcional de GetA que use los mismos o similares parámetros?

type GetA<
  T extends keyof TestObj,
  P extends keyof TestObj[T]
> = TestObj[T][P]["a"];
// Type '"a"' cannot be used to index type 'TestObj[T][P]'.ts(2536)

type TestObj = {
  nested1: {
    prop: {
      a: "foo1";
      b: "bar1";
    };
    anotherProp: {
      a: "foo2";
      b: "bar2"
    }
  };
  nested2: {
    prop: {
      a: "foo3";
      b: "bar3";
    };
  };
};

Respuesta #1

Tenga en cuenta que extends no significa equal. Vea este ejemplo:

type GetA<
  T extends keyof TestObj,
  P extends keyof TestObj[T]
  > = TestObj[T][P]['a'] // expected error

type TestObj = {
  nested1: {
    prop: {
      a: "foo1";
      b: "bar1";
    };
    anotherProp: {
      a: "foo2";
      b: "bar2"
    }
  };
  nested2: {
    prop: {
      a: "foo3";
      b: "bar3";
    };
  };
};

type Result = GetA<'nested1', 'prop' & { tag: 2 }> // unknown

Puedes encontrar más explicación en mi artículo

'prop' & { tag: 2 } se puede asignar a keyof TestObj[T] y al mismo tiempo es un subtipo de prop.

Intenta deshacerte de {tag: 2} y obtendrás el resultado esperado:

type Result = GetA<'nested1', 'prop'> // foo1

Por lo tanto, para hacerlo más seguro, necesitas agregar un tipo condicional:

type GetA<
  T extends keyof TestObj,
  P extends keyof TestObj[T]
  > = 'a' extends keyof TestObj[T][P] ? TestObj[T][P]['a'] : never

type TestObj = {
  nested1: {
    prop: {
      a: "foo1";
      b: "bar1";
    };
    anotherProp: {
      a: "foo2";
      b: "bar2"
    }
  };
  nested2: {
    prop: {
      a: "foo3";
      b: "bar3";
    };
  };
};

type Result = GetA<'nested1', 'prop'> // foo1

Patio de recreo

Además, puedes hacerlo menos verboso:

type GetA<
  T extends keyof TestObj,
  P extends keyof TestObj[T],
  Result = TestObj[T][P]
  > = Result['a' & keyof Result]