简体   繁体   中英

Is it possible to define a function with properties in TypeScript without a type assertion or intermediate?

TypeScript lets you define an interface which is both callable and has properties:

interface FnWithProps {
    (x: number): number;
    a: string;
    b: string;
}

Here's one way to create values assignable to this interface:

function double(x: number) {
    return x * x;
}
double.a = 'Hello';
double.b = 'Goodbye';

let f: FnWithProps = double;  // ok

If double.a or double.b are not set, this triggers an error.

Is it possible to construct such a value directly, without going through an intermediate or using a type assertion?

This isn't valid TypeScript and triggers all sorts of errors:

let g: FnWithProps = {
    (x: number) => x,
    a: 'Hello',
    b: 'Goodbye',
};

I think the intermediate solution is probably the best one as it works well for overloads too, but you can also use Object.assign to get a similar effect :

let f: FnWithProps = Object.assign(function double(x: number) {
    return x * x;
},{
    a : 'Hello',
    b : 'Goodbye'
}); 

Although this does mean we don't get inference for the function params or the properties.

If this is a common scenario for you, we can build a utility function to get inference for everything:

interface FnWithProps {
    (x: number): number;
    a: string;
    b: string;
}

function functionWithProps<T extends (...a: any[]) => any>(fn: (...a: Parameters<T>) => ReturnType<T>, props: Pick<T, keyof T>){
    return Object.assign(fn, props);
}

let f = functionWithProps<FnWithProps>(function double(x) { // annotation not necesary
    return x * x;
},{
    a : 'Hello', // helpful intelisense here
    b : 'Goodbye'
}); 

Edit: Using interface FnWithProps from original post

interface FnWithProps {
    (x: number): number;
    a: number;
    b: number;
}
const g:FnWithProps = (()=>{
    const obj={
        a:2,b:3,
        func:function(x:number){ return this.a*x+this.b}
    }
    return obj.func.bind({a:obj.a,b:obj.b}) as FnWithProps
})()
console.log(g(1))

Demo in typescript playground

The thing to note here is that this for the definition of the function is obj . That's required to prevent errors with reference to this. However, after it is taken out of obj , this this is lost! So it is replaced by using bind. (Assign would have worked as well).

As an aside, an alternative to using interface is as follows:

type Fn = (x:number)=>number
type Props = { a: string, b:string}
const g=(function():Fn & Props{
  const f=(x:number)=>x*x
  f.a='Hello'
  f.b='Goodbye'
  return f  // type checking properly applied here
})()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM