typescript - ¿Cómo crear un tipo que genere una notación de puntos de las propiedades de clases anidadas como literales de cadena?
Esto está relacionado con el SDK de AWS, que tiene varios clientes, en los cuales cada uno hay espacios de nombres con propiedades.
Esta biblioteca exporta AWS, que tiene varios clientes, por ejemplo, DynamoDB y ACM. DynamoDB tiene una propiedad llamada DocumentClient. ACM tiene una propiedad llamada CertificateBody.
class _DocumentClient {}
namespace _DocumentClient {}
// DynamoDB that is imported in the AWS module (see below)
declare class _DynamoDB {}
declare namespace _DynamoDB {
export import DocumentClient = _DocumentClient;
}
// ACM client that is imported in the AWS module (see below)
declare class _ACM {}
declare namespace _ACM {
export type CertificateBody = string;
}
// AWS module (mimicking)
// The original library (as linked above) exports a lot more clients...
namespace AWS {
export import ACM = _ACM;
export import DynamoDB = _DynamoDB;
}
Estoy intentando crear un tipo que genere una unión de literales de cadena como "DynamoDB.DocumentClient" | "ACM.CertificateBody".
He intentado obtener la lista de todas las propiedades de cada cliente, pero eso parece ser una tarea imposible, y mucho menos agregar un cliente y una propiedad con notación de puntos.
Solo logré obtener el literal de cadena prototype en lugar de los literales de cadena DocumentClient y CertificateBody que quería.
// this works - ("ADM" | "DynamoDB")
type ClientName = keyof typeof AWS;
// shouldn't this yield "DocumentClient" | "CertificateBody"? Using this type only means I can use "prototype" :/
type NestedClientName<C extends ClientName> = keyof typeof AWS[C];
// I want to be able to get a type with a union of string literals like `"ACM.CertificateBody" | "DynamoDB.DocumentClient"
type NestedClientFullName<C extends ClientName, NC extends NestedClientName<C>> = `${C}.${NC}`;
Desafortunadamente, ni NestedClientName (y por consiguiente, NestedClientFullName) están dando resultados justos.
Aquí hay un enlace permanente al archivo completo en GitHub para contexto, para mostrar el archivo original aws-sdk. Lo he reproducido en ambos fragmentos de arriba y en el área de pruebas a continuación.
Parque infantil
Respuesta #1
Según el ejemplo de código, ACM en realidad no tiene una propiedad llamada CertificateBody, por lo que lo que estás pidiendo es imposible, al menos para las partes de tu tarea que involucran tipos exportados.
En TypeScript, los namespace pueden actuar tanto como valores que existen en tiempo de ejecución y contienen propiedades, como espacios de nombres para tipos que existen solo en el código TypeScript. Cuando exportas un valor (como un const o una function) desde un namespace, se convierte en una propiedad del valor del namespace. Cuando exportas un tipo (como una interface o type) desde un namespace, no lo hace.
Si tienes un espacio de nombres llamado Foo, entonces typeof Foo te dará el tipo del valor llamado Foo, que contiene cualquier valor exportado como claves de propiedad. No te dará acceso al espacio de nombres de tipos llamado Foo, ni a ninguna forma de listar los tipos que existen allí. De hecho, no hay manera de enumerar tipos en TypeScript:
declare namespace Foo {
export const v: number;
export type T = number;
}
type K = keyof typeof Foo;
// type K = "v"
Solo para dejar muy claro que el tipo y el valor son diferentes: si solo exportas tipos y no valores desde un espacio de nombres, entonces ni siquiera habrá un valor correspondiente al espacio de nombres:
declare namespace Bar {
export const v: number;
}
const okay = Bar; // okay
declare namespace Baz {
export type T = number;
}
const oops = Baz; // error
En lo anterior, typeof Bar existe como un tipo de objeto con una propiedad v cuyo valor es de tipo number, pero typeof Baz no es algo en absoluto. No hay ningún valor llamado Baz cuyo type puedas obtener. Baz es solo un espacio de nombres para tipos.
Entonces, ACM no tiene una propiedad llamada CertificateBody. En cambio, el espacio de nombres ACM tiene un tipo llamado CertificateBody. El tipo se puede referenciar directamente en TypeScript como ACM.CertificateBody, pero no hay manera de acceder a él programáticamente, y definitivamente no como un tipo de literal de cadena como "CertificateBody". Desafortunadamente, lo que estás tratando de hacer es imposible.
Enlace de Playground al código
