Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript declaring a function with properties

Tags:

typescript

I'm writing a library and I want to port it to typescript.

Currently it look something like that:

index.js

const is = value => {
    ... do some returns here
}

is.number = x => typeof x === 'number'
is.bla = x => typeof x === 'bla'

and so on.

I have wrote an interface describing is and all its methods.

type TypeQueryMethod = (val: any) => boolean;

interface Is {
    (val: any): string;
    undefined: TypeQueryMethod;
    null: TypeQueryMethod;
    ...
}

When I try to mark is with the type: const is: Is = value => ...

It throws an error:

Type '(value: any) => string' is not assignable to type 'Is'.
Property 'undefined' is missing in type '(value: any) => string'.

Which makes sense because the declaration of the object is split.

How do you construct such an object that is both a method, and has methods?

like image 668
Giora Guttsait Avatar asked Sep 30 '17 10:09

Giora Guttsait


2 Answers

Update for TypeScript 3.1+

TypeScript 3.1 introduced support for declaring properties on functions, to allow you to do this the way you were doing it to begin with:

const is: Is = (val: any) => typeof val; // okay
is.undefined = (val: any) => typeof val === 'undefined'; 
is.null = (val: any) => (val === null)

For TypeScript versions before 3.1:

If you want to make the type checker happy, you can use Object.assign() to return a fully-formed Is object without building it in stages:

const is: Is = Object.assign(
  (val: any) => typeof val,
  {
    undefined: (val: any) => typeof val === 'undefined',
    null: (val: any) => (val === null)
    // ...
  }
);

Of course, if you don't want to change the structure of your code, then you can do as @Saravana suggests and use a type assertion to just inform the type checker that is is definitely an Is, even though it technically isn't one until you're done building it.

Either way works, but I prefer the Object.assign() method because the type checker will warn you if you neglect to implement something:

// error, "undefined" is missing
const is: Is = Object.assign(
  (val: any) => typeof val,
  {
    null: (val: any) => (val === null)
    // ...
  }
);

while the type assertion method will not:

const is = ((val: any) => typeof val) as any as Is;
is.null = (val) => val === null;
// no error

whereas the type assertion method will not. It's up to you.


Playground link to code

like image 52
jcalz Avatar answered Nov 15 '22 06:11

jcalz


You cannot implement a function as well as its properties at the same time. You can define the function first and assert it to Is and define the rest of the methods:

const is = ((val: any) => typeof (val)) as any as Is;

is.null = (val) => true;
is.undefined = (val) => true;

Or use a factory function to create Is:

function createIs(): Is {
    const is = ((val: any) => {
        return "";
    }) as Is;
    is.null = (val) => true;
    is.undefined= (val) => true;
    return is;
}

const is: Is = createIs();
like image 43
Saravana Avatar answered Nov 15 '22 06:11

Saravana