Construire un objet de fonction avec des propriétés dactylographiée

voix
46

Je veux créer un objet de fonction, qui a également des propriétés détenues sur elle. Par exemple en JavaScript que je ferais:

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

Maintenant, je peux dactylographiée décrire le type de ce que:

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

Cependant, je ne peux pas construire réellement, sans avoir besoin d'un plâtre. Tel que:

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

Comment voulez-vous construire cela sans un casting?

Créé 07/10/2012 à 07:08
source utilisateur
Dans d'autres langues...                            


9 réponses

voix
73

Mise à jour: Cette réponse était la meilleure solution dans les versions antérieures dactylographiées, mais il y a de meilleures options disponibles dans les versions plus récentes (voir d'autres réponses).

La réponse acceptée fonctionne et peut-être nécessaire dans certaines situations, mais ont l'inconvénient de fournir aucune sécurité de type pour la construction de l'objet. Cette technique au moins jeter une erreur de type si vous tentez d'ajouter une propriété non définie.

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

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

// type error
f.notDeclard = 3
Créé 05/09/2013 à 16:12
source utilisateur

voix
34

Tapuscrit est conçu pour traiter ce cas par la fusion de la déclaration :

vous pouvez aussi être familier avec la pratique JavaScript de créer une fonction, puis l'extension de la fonction supplémentaire en ajoutant des propriétés sur la fonction. Tapuscrit utilise la fusion de déclaration pour construire des définitions comme celle-ci d'une manière de type sécurisé.

la fusion de déclaration nous permet de dire que quelque chose est à la fois une fonction et un espace de noms (module interne):

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

Cela préserve dactylographie et nous permet d' écrire à la fois f()et f.someValue. Lors de l' écriture d' un .d.tsfichier pour le code existant JavaScript, utilisez declare:

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

Ajout de propriétés à des fonctions est souvent un motif confus ou inattendu dactylographiée, alors essayez de l'éviter, mais il peut être nécessaire lors de l'utilisation ou la conversion du code JS ancien. Ceci est l'une des seules fois qu'il serait approprié de mélanger des modules internes (espaces de noms) avec externe.

Créé 28/10/2015 à 13:47
source utilisateur

voix
27

Ceci est facilement réalisable maintenant (tapuscrit 2.x) avec Object.assign(target, source)

Exemple:

entrez la description d'image ici

La magie est ici qui Object.assign<T, U>(...)est tapé pour retourner une intersection type de T & U.

Que cela résout l'application d'une interface connue est directe aussi:

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

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

Erreur: foo: nombre incessible à foo: chaîne
Erreur: numéro incessible à string [] (type de retour)

mise en garde: vous devrez peut - être Polyfill Object.assign si ciblant les navigateurs plus anciens.

Créé 25/01/2017 à 13:43
source utilisateur

voix
17

Donc, si l'exigence est de construire simplement et attribuer cette fonction à « f » sans un casting, voici une solution:

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

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

Pour l'essentiel, il utilise une fonction d'auto exécution littérale de « construire » un objet qui correspondra à cette signature avant la cession est faite. La seule bizarrerie est que la déclaration interne de la fonction doit être de type « tout », sinon le compilateur crie que vous affectez à une propriété qui n'existe pas encore sur l'objet.

EDIT: simplifié un peu le code.

Créé 07/10/2012 à 17:33
source utilisateur

voix
1

Je ne peux pas dire qu'il est très simple mais il est certainement possible:

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

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

si vous avez curieux c'est d'un point essentiel de la mine avec la version tapuscrit / JavaScript deOptional

Créé 07/02/2018 à 23:57
source utilisateur

voix
1

Une réponse mise à jour: depuis l'ajout de types d'intersection via &, il est possible de « fusionner » deux types inférées à la volée.

Voici une aide générale qui lit les propriétés d'un objet fromet les copie sur un objet onto. Il retourne le même objet , ontomais avec un nouveau type qui comprend deux ensembles de propriétés, décrivant de manière correctement le comportement d'exécution:

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

Cette aide de bas niveau ne fonctionne toujours un type d'affirmation, mais il est de type sécurisé par la conception. Avec cette aide en place, nous avons un opérateur que nous pouvons utiliser pour résoudre le problème de l'OP avec la sécurité complète du type:

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}`)
        }
    }
);

Cliquez ici pour l' essayer dans l'aire de jeu tapuscrit . Notez que nous avons contraint fooà être de type Foo, de sorte que le résultat de mergedoit être complète Foo. Donc , si vous renommez barpour badvous obtenez une erreur de type.

NB Il y a encore un trou de type ici, cependant. Tapuscrit ne fournit pas un moyen de contraindre un paramètre de type à « pas une fonction ». Donc , vous pourriez obtenir confus et passer votre fonction en tant que deuxième argument merge, et ne fonctionnerait pas. Donc , jusqu'à ce que cela peut être déclaré, nous devons l' attraper à l' exécution:

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;
}
Créé 10/03/2016 à 19:14
source utilisateur

voix
1

Comme un raccourci, vous pouvez affecter dynamiquement la valeur d'objet en utilisant la [ « propriété »] accesseur:

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

Cela permet de contourner la vérification de type. Cependant, il est assez sûr parce que vous devez accéder intentionnellement la propriété de la même façon:

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

Cependant, si vous voulez vraiment le type de vérification de la valeur de la propriété, cela ne fonctionnera pas.

Créé 11/10/2012 à 05:38
source utilisateur

voix
0

Vieille question, mais pour les versions dactylographiées commençant par 3.1, vous pouvez simplement faire la cession de la propriété comme vous le feriez dans JS plaine, aussi longtemps que vous utilisez une déclaration de fonction ou le constmot - clé pour votre 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'"

Référence et par exemple en ligne .

Créé 26/11/2018 à 21:38
source utilisateur

voix
0

Cette part de frappe fort, mais vous pouvez faire

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

si vous essayez d'obtenir le typage fort autour oppressive comme je l'étais quand je trouvais cette question. Malheureusement, c'est un tapuscrit cas d 'échec JavaScript parfaitement valide, donc vous devez vous dire tapuscrit de faire marche arrière.

« JavaScript Vous est parfaitement valide tapuscrit » correspond à false. (Note: en utilisant 0,95)

Créé 21/02/2014 à 16:59
source utilisateur

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