typescript - nodejs - Valores predeterminados de la interfaz de mecanografía
typescript version (6)
¿Puedo decirle a la interfaz que predetermina las propiedades que no proporciono a nulo? ¿Qué me dejaría hacer esto?
No. Pero, por defecto, no están
undefined
que en general está bien.
Puede usar el siguiente patrón, es decir, tener una aserción de tipo en el punto de creación:
let x: IX = {} as any;
x.a = ''xyz''
x.b = 123
x.c = new AnotherType()
Tengo este y otros patrones documentados aquí: basarat.gitbooks.io/typescript/content/docs/tips/…
Tengo la siguiente interfaz en TypeScript:
interface IX {
a: string,
b: any,
c: AnotherType
}
Declaro una variable de ese tipo e inicializo todas las propiedades
let x: IX = {
a: ''abc'',
b: null,
c: null
}
Luego les asigno valores reales en una función init más tarde
x.a = ''xyz''
x.b = 123
x.c = new AnotherType()
Pero no me gusta tener que especificar un grupo de valores nulos predeterminados para cada propiedad al declarar el objeto cuando se establecerán más tarde en valores reales. ¿Puedo decirle a la interfaz que predetermina las propiedades que no proporciono a nulo? ¿Qué me dejaría hacer esto?
let x: IX = {
a: ''abc''
}
sin obtener un error del compilador. Ahora me dice
TS2322: El tipo ''{}'' no es asignable al tipo ''IX''. Falta la propiedad ''b'' en el tipo ''{}''.
Me topé con esto mientras buscaba una forma mejor de lo que había llegado. Después de leer las respuestas y probarlas, pensé que valía la pena publicar lo que estaba haciendo, ya que las otras respuestas no me parecieron tan breves. Para mí era importante tener que escribir una pequeña cantidad de código cada vez que configuraba una nueva interfaz. Me decidí por ...
Utilizando una función genérica personalizada de deepCopy:
deepCopy = <T extends {}>(input: any): T => {
return JSON.parse(JSON.stringify(input));
};
Define tu interfaz
interface IX {
a: string;
b: any;
c: AnotherType;
}
... y define los valores predeterminados en una const separada.
const XDef : IX = {
a: '''',
b: null,
c: null,
};
Entonces inicie así:
let x : IX = deepCopy(XDef);
Eso es todo lo que se necesita ...
.. sin embargo ..
Si desea inicializar cualquier elemento raíz , puede modificar la función deepCopy para aceptar valores predeterminados personalizados. La función se convierte en:
deepCopyAssign = <T extends {}>(input: any, rootOverwrites?: any): T => {
return JSON.parse(JSON.stringify({ ...input, ...rootOverwrites }));
};
Que luego se puede llamar así:
let x : IX = deepCopyAssign(XDef, { a:''customInitValue'' } );
Cualquier otra forma preferida de copia profunda funcionaría.
Si solo se necesita una copia superficial, Object.assign sería suficiente, dejando de lado la necesidad de la función
deepCopy
o
deepCopyAssign
.
let x : IX = object.assign({}, XDef, { a:''customInitValue'' });
Problemas conocidos
-
No se asignará en profundidad de esta manera, pero no es demasiado difícil modificar
deepCopyAssign
para iterar y verificar los tipos antes de asignar. - El proceso de análisis / stringify perderá funciones y referencias. No los necesito para mi tarea y tampoco el OP.
- Los valores de inicio personalizados no son insinuados por el IDE o el tipo verificado cuando se ejecuta.
No puede establecer valores predeterminados en una interfaz, pero puede lograr lo que desea hacer utilizando Propiedades opcionales (compare el párrafo 3):
https://www.typescriptlang.org/docs/handbook/interfaces.html
Simplemente cambie la interfaz a:
interface IX {
a: string,
b?: any,
c?: AnotherType
}
Entonces puedes hacer:
let x: IX = {
a: ''abc''
}
Y use su función init para asignar valores predeterminados a
xb
y
xc
si esas propiedades no están establecidas.
Puede implementar la interfaz con una clase, luego puede tratar de inicializar los miembros en el constructor:
class IXClass implements IX {
a: string;
b: any;
c: AnotherType;
constructor(obj: IX);
constructor(a: string, b: any, c: AnotherType);
constructor() {
if (arguments.length == 1) {
this.a = arguments[0].a;
this.b = arguments[0].b;
this.c = arguments[0].c;
} else {
this.a = arguments[0];
this.b = arguments[1];
this.c = arguments[2];
}
}
}
Otro enfoque es usar una función de fábrica:
function ixFactory(a: string, b: any, c: AnotherType): IX {
return {
a: a,
b: b,
c: c
}
}
Entonces puedes simplemente:
var ix: IX = null;
...
ix = new IXClass(...);
// or
ix = ixFactory(...);
Puede usar el tipo de mapa
Partial
como se explica en la documentación:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html
En tu ejemplo, tendrás:
interface IX {
a: string;
b: any;
c: AnotherType;
}
let x: Partial<IX> = {
a: ''abc''
}
Si bien la respuesta de @ Timar funciona perfectamente para los valores predeterminados
null
(lo que se solicitó), aquí hay otra solución fácil que permite otros valores predeterminados: definir una interfaz de opción, así como una constante correspondiente que contenga los valores predeterminados;
en el constructor use el
operador de propagación
para establecer la variable miembro de
options
interface IXOptions {
a?: string,
b?: any,
c?: number
}
const XDefaults: IXOptions = {
a: "default",
b: null,
c: 1
}
export class ClassX {
private options: IXOptions;
constructor(XOptions: IXOptions) {
this.options = { ...XDefaults, ...XOptions };
}
public printOptions(): void {
console.log(this.options.a);
console.log(this.options.b);
console.log(this.options.c);
}
}
Ahora puedes usar la clase así:
const x = new ClassX({ a: "set" });
x.printOptions();
Salida:
set
null
1