Build a function object with properties in TypeScript

后端 未结 9 1294
南旧
南旧 2020-11-28 06:00

I want to create a function object, which also has some properties held on it. For example in JavaScript I would do:

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


        
9条回答
  •  春和景丽
    2020-11-28 06:32

    An updated answer: since the addition of intersection types via &, it is possible to "merge" two inferred types on the fly.

    Here's a general helper that reads the properties of some object from and copies them over an object onto. It returns the same object onto but with a new type that includes both sets of properties, so correctly describing the runtime behaviour:

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

    This low-level helper does still perform a type-assertion, but it is type-safe by design. With this helper in place, we have an operator that we can use to solve the OP's problem with full type safety:

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

    Click here to try it out in the TypeScript Playground. Note that we have constrained foo to be of type Foo, so the result of merge has to be a complete Foo. So if you rename bar to bad then you get a type error.

    NB There is still one type hole here, however. TypeScript doesn't provide a way to constrain a type parameter to be "not a function". So you could get confused and pass your function as the second argument to merge, and that wouldn't work. So until this can be declared, we have to catch it at runtime:

    function merge(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;
    }
    

提交回复
热议问题