Tipos condicionales de TypeScript: filtre las propiedades de solo lectura/seleccione solo las propiedades requeridas
typescript2.8 conditional-types (1)
Usando los nuevos tipos condicionales en TypeScript (o tal vez otra técnica), ¿hay alguna manera de elegir solo ciertas propiedades de una interfaz en función de sus modificadores? Por ejemplo, tener ...
interface I1 {
readonly n: number
s: string
}
Me gustaría crear un nuevo tipo basado en el anterior que se ve así:
interface I2 {
s: string
}
Actualización 2018-10:
@MattMcCutchen
ha descubierto que
es
posible detectar campos de
readonly
(invalidando el pasaje tachado a continuación), como se muestra en
esta respuesta
.
Aquí hay una manera de construirlo:
type IfEquals<X, Y, A=X, B=never> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T];
Si desea extraer los campos de escritura de una interfaz, puede usar la definición de
WritableKeys
anterior y
Pick
juntos:
interface I1 {
readonly n: number
s: string
}
type I2 = Pick<I1, WritableKeys<I1>>;
// equivalent to { s: string; }
¡Hurra!
Solo para
readonly
, no creo que puedas extraerlos.
He
examinado este problema antes
y no fue posible entonces;
Y no creo que nada haya cambiado.
Como
el compilador no comprueba las propiedades de
readonly
, siempre puede asignar un
{readonly n: number}
a un
{n: number}
y viceversa.
Y por lo tanto, la obvia verificación de tipo condicional TSv2.8 no funciona.
Si, por ejemplo,
{n: number}
no se consideraba asignable a
{readonly n: number}
, podría hacer algo como:
// does not work, do not try this
type ExcludeReadonlyProps<T> = Pick<T,
{ [K in keyof T]-?:
({ readonly [P in K]: T[K] } extends { [P in K]: T[K] } ? never : K)
}[keyof T]>
type I2 = ExcludeReadonlyProps<I1> // should be {s: string} but is {} 🙁
Pero no puedes.
Hay una discusión interesante sobre esto en un
tema de GitHub originalmente llamado "los modificadores de
readonly
son una broma"
.
¡Lo siento! Buena suerte.
Para propiedades opcionales, puede detectarlas y, por lo tanto, extraerlas o excluirlas.
La idea aquí es que
{}
extiende
{a?: string}
, pero
{}
no extiende
{a: string}
o incluso
{a: string | undefined}
{a: string | undefined}
.
A continuación, le mostramos cómo puede crear una forma de eliminar propiedades opcionales de un tipo:
type RequiredKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? never : K)
}[keyof T]
type OptionalKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? K : never)
}[keyof T]
type ExcludeOptionalProps<T> = Pick<T, RequiredKeys<T>>
type I3 = {
a: string,
b?: number,
c: boolean | undefined
}
type I4 = ExcludeOptionalProps<I3>;
// {a: string; c: boolean | undefined} 🙂
Eso está bien.
Finalmente, no sé si quieres poder hacer cosas con los modificadores de propiedad de clase como
public
,
private
,
protected
y
abstract
, pero dudaría de ello.
Sucede que las propiedades de clase
private
y
protected
se pueden
excluir con
bastante facilidad, ya que no están presentes en
keyof
:
class Foo {
public a = ""
protected b = 2
private c = false
}
type PublicOnly<T> = Pick<T, keyof T>; // seems like a no-op but it works
type PublicFoo = PublicOnly<Foo>; // {a: string} 🙂
Pero
extraer
las propiedades
private
o
protected
puede ser imposible, por la misma razón que excluirlas es tan fácil:
keyof Foo
no las tiene.
Y para todo esto, incluido el
abstract
, no puede agregarlos a las propiedades en alias de tipo (son modificadores de clase), por lo que no hay mucho que se me ocurra para tocarlos.
De acuerdo, espero que eso ayude.