12

I have this interface:

interface IParameters {
    form: number;
    field: string;
    ...
}

I want the formproperty to be number or function and field to be string or function.

I try something like this:

interface IParameters {
    form: number | Function;
    field: string | Function;
    ...
}

I need this because in my code i use this variables like this:

var form = (typeof _oParameters.form === "function" ? _oParameters.form() : _oParameters.form);
var field = (typeof _oParameters.field === "function" ? _oParameters.field() : _oParameters.field);

I don't want change this variable in all my code from string/number to default function and to prevent setting this variables to other types.

but if I try to call one of this two variable like function:

var param:IParameters;
param.form();
...

I get this error:

Cannot invoke an expression whose type lacks a call signature.

but param.form = 12; works.

The only solution that i have found is to declare form and field to any type.

Is other way to define this variable without any type?

3 Answers 3

20

If you try to use the code from seriesOne you will notice, that you cannot assign form (or field) a value that isn't a function. You would get a
Type '{ form: string; }' is not assignable to type 'IParameters'. Types of property 'form' are incompatible. Type 'string' is not assignable to type '() => string'.

I found that using form: (() => string) | string; resolves this problem.

You can try the code at the typescript playground.

Working sample:

    interface IParameters {
        form: (() => string) | string;
    }

    function strFunct(): string {
        return 'Hello TypeScript';
    }

    function test() {
        let paramA: IParameters = {
            form: strFunct
        }

        let paramB: IParameters = {
            form: 'Hello stackoverflow'
        }
    }

    class Foo {
        constructor(param: IParameters) {
            var x = typeof param.form === 'string' ? param.form : param.form();
        }
    }
Sign up to request clarification or add additional context in comments.

2 Comments

Woah! Th parenthesis between the function on IParameters did the trick!!
This. Oddly if you write ()=>string|string the compiler will allow it but classes which consume it will only expect ()=>string and will not be allowed to pass a string literal. On the other hand if you reverse the order and write string|()=>string typescript won't parse it. Putting the parentheses around the call signature it fixes both cases.
7

Perhaps if you define your union type using a call signature rather than Function, like so...

interface IParameters {
    // form is of type function that returns number, or number literal.
    form: () => number | number;

    // field is of type function that returns a string, or string literal.
    field: () => string | string;
}

class Foo {
    constructor (param: IParameters) {
        var x = typeof param.form === "number" ? param.form : param.form();
        var y = typeof param.field === "string" ? param.field : param.field();
    }
}

Here, I am still checking form and field using typeof, however TypeScript is happy for it to be either a function call, or a value.

Comments

2

Upd
That behavior appears to be an issue #3812 with TypeScript.
It is to be fixed as a part of TypeScript 2.0 according to the milestone assigned.

Original answer

You could use instanceof as a type guard like

var field = (_oParameters.field instanceof Function ? _oParameters.field() : _oParameters.field);

3 Comments

if i use your code i get the same error Cannot invoke an expression whose type lacks a call signature. with this define form: number | Function;
You're right. That appeared to be an issue with TypeScript. It'll hopefully be fixed at v2.0.
is it fixed now?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.