Construir un objeto de función con propiedades a máquina de escribir

votos
46

Quiero crear un objeto función, que también tiene algunas propiedades mantenidas en él. Por ejemplo en JavaScript que haría:

var f = function() { }
f.someValue = 3;

Ahora a máquina de escribir que puedo describir el tipo de esto como:

var f: { (): any; someValue: number; };

Sin embargo no puedo realmente construirlo, sin que se requiera un yeso. Como:

var f: { (): any; someValue: number; } =
    <{ (): any; someValue: number; }>(
        function() { }
    );
f.someValue = 3;

¿Cómo se puede construir esto sin un yeso?

Publicado el 07/10/2012 a las 07:08
fuente por usuario
En otros idiomas...                            


9 respuestas

votos
73

Actualización: Esta respuesta fue la mejor solución en versiones anteriores de mecanografiado, pero hay mejores opciones disponibles en las versiones más recientes (ver otras respuestas).

La respuesta aceptada que funciona y puede ser necesaria en algunas situaciones, pero tienen el inconveniente de proporcionar ningún tipo de seguridad para la edificación del objeto. Esta técnica será al menos lanzar un error de tipo si intenta agregar una propiedad no definida.

interface F { (): any; someValue: number; }

var f = <F>function () { }
f.someValue = 3

// type error
f.notDeclard = 3
Respondida el 05/09/2013 a las 16:12
fuente por usuario

votos
34

Mecanografiado está diseñado para manejar este caso a través de la fusión de declaración :

También puede estar familiarizado con la práctica JavaScript de crear una función y luego se extiende la función aún más mediante la adición de propiedades en la función. Mecanografiado utiliza fusión declaración para construir las definiciones de este tipo en una forma de tipo seguro.

Declaración fusión nos permite afirmar que algo es a la vez una función y un espacio de nombres (módulo interno):

function f() { }
namespace f {
    export var someValue = 3;
}

Esto preserva escribir y nos permite escribir tanto f()y f.someValue. Al escribir un .d.tsarchivo de código JavaScript existente, utilice declare:

declare function f(): void;
declare namespace f {
    export var someValue: number;
}

Adición de propiedades a las funciones es a menudo un patrón confuso o inesperado a máquina, a fin de tratar de evitarlo, pero puede ser necesario cuando se utiliza o convertir más viejo código JS. Esta es una de las pocas veces que sería apropiado para mezclar módulos internos (espacios de nombres) con externo.

Respondida el 28/10/2015 a las 10:47
fuente por usuario

votos
27

Esto es fácilmente alcanzable ahora (2.x mecanografiado) con Object.assign(target, source)

ejemplo:

introducir descripción de la imagen aquí

La magia aquí es que Object.assign<T, U>(...)se escribe para devolver una intersección tipo de T & U.

Hacer cumplir que esto se resuelve con una interfaz conocida también es directa:

interface Foo {
  (a: number, b: string): string[];
  foo: string;
}

let method: Foo = Object.assign(
  (a: number, b: string) => { return a * a; },
  { foo: 10 }
); 

Error: foo: número no asignable a foo: Cadena de
error: número no asignable a string [] (tipo de retorno)

advertencia: puede que tenga que polyfill Object.assign si apuntan a los navegadores antiguos.

Respondida el 25/01/2017 a las 10:43
fuente por usuario

votos
17

Así que si el requisito es simplemente construir y asignar dicha función a "f" sin una conversión, que aquí hay una posible solución:

var f: { (): any; someValue: number; };

f = (() => {
    var _f : any = function () { };
    _f.someValue = 3;
    return _f;
})();

En esencia, se utiliza una función de auto ejecución literal a "construir" un objeto que va a coincidir con la firma antes de realizar la tarea. La única rareza es que la declaración interna de la función necesita ser de tipo 'cualquiera', de lo contrario el compilador grita que está asignando a una propiedad que no existe en el objeto todavía.

EDIT: simplificado el código un poco.

Respondida el 07/10/2012 a las 17:33
fuente por usuario

votos
1

No puedo decir que es muy sencillo pero es definitivamente posible:

interface Optional {
  <T>(value?: T): OptionalMonad<T>;
  empty(): OptionalMonad<any>;
}

const Optional = (<T>(value?: T) => OptionalCreator(value)) as Optional;
Optional.empty = () => OptionalCreator();

si tienes curiosidad se trata de una esencia de la mina con la versión mecanografiada / JavaScript deOptional

Respondida el 07/02/2018 a las 20:57
fuente por usuario

votos
1

Una respuesta actualización: ya que la adición de tipos de intersecciones a través &, es posible "fusionar" dos tipos inferidos sobre la marcha.

Aquí está un ayudante general de que lee las propiedades de un objeto fromy los copia sobre un objeto onto. Se devuelve el mismo objeto onto, pero con un nuevo tipo que incluye dos conjuntos de propiedades, que describe tan correctamente el comportamiento de tiempo de ejecución:

function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
    Object.keys(from).forEach(key => onto[key] = from[key]);
    return onto as T1 & T2;
}

Este ayudante de bajo nivel tiene todavía realizar un tipo de afirmación, pero es de tipo seguro por diseño. Con esta ayuda en su lugar, tenemos un operador que podemos utilizar para resolver el problema de la OP con la seguridad de tipo completo:

interface Foo {
    (message: string): void;
    bar(count: number): void;
}

const foo: Foo = merge(
    (message: string) => console.log(`message is ${message}`), {
        bar(count: number) {
            console.log(`bar was passed ${count}`)
        }
    }
);

Haga clic aquí para probarlo en el mecanografiado de juegos . Obsérvese que se han limitado fooa ser de tipo Foo, por lo que el resultado de mergetiene que ser una completa Foo. Así que si cambia el nombre bara badcontinuación, se obtiene un error de tipo.

NB Todavía hay un agujero tipo aquí, sin embargo. Mecanografiado no proporciona una manera de limitar un parámetro de tipo de "No es una función". Por lo que podría confundirse y pasar su función como segundo argumento merge, y que no iba a funcionar. Así que hasta que esto puede ser declarada, tenemos que cogerlo en tiempo de ejecución:

function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
    if (typeof from !== "object" || from instanceof Array) {
        throw new Error("merge: 'from' must be an ordinary object");
    }
    Object.keys(from).forEach(key => onto[key] = from[key]);
    return onto as T1 & T2;
}
Respondida el 10/03/2016 a las 16:14
fuente por usuario

votos
1

Como atajo, puede asignar dinámicamente el valor del objeto utilizando el [ 'propiedad'] de acceso:

var f = function() { }
f['someValue'] = 3;

Esto no pasa por la comprobación de tipos. Sin embargo, es bastante seguro ya que hay que acceder intencionalmente la propiedad de la misma manera:

var val = f.someValue; // This won't work
var val = f['someValue']; // Yeah, I meant to do that

Sin embargo, si realmente desea que el tipo de comprobación de valor de la propiedad, esto no va a funcionar.

Respondida el 11/10/2012 a las 05:38
fuente por usuario

votos
0

Cuestión de edad, sino también para las versiones escritas a máquina a partir de 3,1, sólo tiene que hacer la asignación de propiedad como lo haría en la llanura JS, siempre y cuando se utiliza una declaración de la función o de la constpalabra clave de la variable:

function f () {}
f.someValue = 3; // fine
const g = function () {};
g.someValue = 3; // also fine
var h = function () {};
h.someValue = 3; // Error: "Property 'someValue' does not exist on type '() => void'"

Referencia y ejemplo en línea .

Respondida el 26/11/2018 a las 21:38
fuente por usuario

votos
0

Esto se aparta de tipado fuerte, pero se puede hacer

var f: any = function() { }
f.someValue = 3;

si usted está tratando de obtener la tipificación fuerte alrededor opresivo como estaba cuando me encontré con esta pregunta. Lamentablemente este es un caso mecanografiado falla en JavaScript perfectamente válido lo que tiene que decirle mecanografiado a retroceder.

"Usted JavaScript es perfectamente válida mecanografiado" se evalúa como falsa. (Nota: el uso de 0,95)

Respondida el 21/02/2014 a las 16:59
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more