dmr
dmr

Reputation: 22333

Set type for function parameters?

Is there a way to let a javascript function know that a certain parameter is of a certain type?

Being able to do something like this would be perfect:

function myFunction(Date myDate, String myString)
{
    //do stuff
}

Thank you!

Update: Being that the answer is a resounding "no," if I want myDate to be treated as a date (in order to call date functions on it), I have to cast it as a date inside the function or set a new variable of type Date to it?

Upvotes: 289

Views: 393458

Answers (14)

pronvit
pronvit

Reputation: 4279

No, plain JavaScript is not a statically typed language. Sometimes you may need to manually check types of parameters in your function body.

However, you can use linters and supersets of JavaScript (like TypeScript, to emulate that behaviour or at least type hint, as seen in some of the other answers.

Upvotes: 297

phil294
phil294

Reputation: 10852

Maybe a helper function like this. But if you see yourself using such syntax regularly, you should probably switch to TypeScript.

function check(caller_args, ...types) {
    if(!types.every((type, index) => {
        if(typeof type === 'string')
            return typeof caller_args[index] === type
        return caller_args[index] instanceof type;
    })) throw Error("Illegal argument given");
}

function abc(name, id, bla) {
   check(arguments, "string", "number", MyClass)
   // code
}

Upvotes: 0

eolsson
eolsson

Reputation: 12727

Not in JavaScript itself, but using Google Closure Compiler's advanced mode, you can do that:

/**
 * @param {Date} myDate The date
 * @param {string} myString The string
 */
function myFunction(myDate, myString)
{
    //do stuff
}

See https://code.google.com/closure/compiler/docs/js-for-compiler.html

Upvotes: 208

xgqfrms
xgqfrms

Reputation: 12146

TypeScript is one of the best solutions for now.

TypeScript extends JavaScript by adding types to the language.

typescript version demo

// type alias
type myDateType = Date;
type myStringType = string;

function myFunction(myDate: myDateType, myString: myStringType) {
  // do stuff
  console.log(`myDate =`, myDate);
  console.log(`myString =`, myString);
}

myFunction(new Date(), 'TypeScript is awesome!');

try this playground online

refs

https://www.typescriptlang.org/

Upvotes: 11

Carson
Carson

Reputation: 7978

I assume you allow IDE to help you; then the below answer may help you.

IDE: jetbrains/Golang That's ok if your IDE is not this. I believe all the IDE which support JSDoc, and then it can satisfy you most request.

and it can show JSDoc very well.

Demo

my /pkg/encoding/base64.js

/**
 * Convert string to the base64 format.
 *
 * @param str {string} Input string
 * @returns {string} some message about return...
 * @example
 *  - btoa(toBinary("☸☹☺☻☼☾☿"))
 *  - Str2base64("☸☹☺☻☼☾☿")
 * @see https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa#unicode_strings
 */
export function Str2base64(str) {
  return btoa(toBinary(str))
}

test.js

import * as base64 from "../pkg/encoding/base64"
const filenameB64 = base64.Str2base64("test")

enter image description here

Useful JSDoc Documentation

Other links

Upvotes: 0

Pouria Moosavi
Pouria Moosavi

Reputation: 730

Explanation

I'm not sure if my answer is direct answer to original question, but as I suppose a lot of people come here to just find a way to tell their IDEs to understand types, I'll share what I found.

If you want to tell VSCode to understand your types, do as follows. Please pay attention that js runtime and NodeJS does not care about these types at all.

Solution

1- Create a file with .d.ts ending: e.g: index.d.ts. You can create this file in another folder. for example: types/index.d.ts
2- Suppose we want to have a function called view. Add these lines to index.d.ts:

/**
 * Use express res.render function to render view file inside layout file.
 *
 * @param {string} view The path of the view file, relative to view root dir.
 * @param {object} options The options to send to view file for ejs to use when rendering.
 * @returns {Express.Response.render} .
 */
view(view: string, options?: object): Express.Response.render;

3- Create a jsconfig.json file in you project's root. (It seems that just creating this file is enough for VSCode to search for your types).

A bit more

Now suppose we want to add this type to another library types. (As my own situation). We can use some ts keywords. And as long as VSCode understands ts we have no problem with it.
For example if you want to add this view function to response from expressjs, change index.d.ts file as follows:

export declare global {
  namespace Express {
    interface Response {
      /**
       * Use express res.render function to render view file inside layout file.
       *
       * @param {string} view The path of the view file, relative to view root dir.
       * @param {object} options The options to send to view file for ejs to use when rendering.
       * @returns {Express.Response.render} .
       */
      view(view: string, options?: object): Express.Response.render;
    }
  }
}

Result

enter image description here

enter image description here

Upvotes: 3

undefined
undefined

Reputation: 6453

Edit: Seven years later, this answer still gets occasional upvotes. It's fine if you are looking for runtime checking, but I would now recommend compile-time type checking using Typescript, or possibly Flow. See https://stackoverflow.com/a/31420719/610585 above for more.

Original answer:

It's not built into the language, but you can do it yourself quite easily. Vibhu's answer is what I would consider the typical way of type checking in Javascript. If you want something more generalized, try something like this: (just an example to get you started)

typedFunction = function(paramsList, f){
    //optionally, ensure that typedFunction is being called properly  -- here's a start:
    if (!(paramsList instanceof Array)) throw Error('invalid argument: paramsList must be an array');

    //the type-checked function
    return function(){
        for(var i=0,p,arg;p=paramsList[i],arg=arguments[i],i<paramsList.length; i++){
            if (typeof p === 'string'){
                if (typeof arg !== p) throw new Error('expected type ' + p + ', got ' + typeof arg);
            }
            else { //function
                if (!(arg instanceof p)) throw new Error('expected type ' + String(p).replace(/\s*\{.*/, '') + ', got ' + typeof arg);
            }
        }
        //type checking passed; call the function itself
        return f.apply(this, arguments);
    }
}

//usage:
var ds = typedFunction([Date, 'string'], function(d, s){
    console.log(d.toDateString(), s.substr(0));
});

ds('notadate', 'test');
//Error: expected type function Date(), got string
ds();
//Error: expected type function Date(), got undefined
ds(new Date(), 42);
//Error: expected type string, got number
ds(new Date(), 'success');
//Fri Jun 14 2013 success

Upvotes: 17

Dan Dascalescu
Dan Dascalescu

Reputation: 151998

While you can't inform JavaScript the language about types, you can inform your IDE about them, so you get much more useful autocompletion.

Here are two ways to do that:

  1. Use JSDoc, a system for documenting JavaScript code in comments. In particular, you'll need the @param directive:

    /**
     * @param {Date} myDate - The date
     * @param {string} myString - The string
     */
    function myFunction(myDate, myString) {
      // ...
    }
    

    You can also use JSDoc to define custom types and specify those in @param directives, but note that JSDoc won't do any type checking; it's only a documentation tool. To check types defined in JSDoc, look into TypeScript, which can parse JSDoc tags.

  2. Use type hinting by specifying the type right before the parameter in a
    /* comment */:

    JavaScript type hinting in WebStorm

    This is a pretty widespread technique, used by ReactJS for instance. Very handy for parameters of callbacks passed to 3rd party libraries.

TypeScript

For actual type checking, the closest solution is to use TypeScript, a (mostly) superset of JavaScript. Here's TypeScript in 5 minutes.

Upvotes: 131

LouT
LouT

Reputation: 1

I've been thinking about this too. From a C background, you can simulate function return code types, as well as, parameter types, using something like the following:

function top_function() {
    var rc;
    console.log("1st call");
    rc = Number(test_function("number", 1, "string", "my string"));
    console.log("typeof rc: " + typeof rc + "   rc: " + rc);
    console.log("2nd call");
    rc = Number(test_function("number", "a", "string", "my string"));
    console.log("typeof rc: " + typeof rc + "   rc: " + rc);
}
function test_function(parm_type_1, parm_val_1, parm_type_2, parm_val_2) {
    if (typeof parm_val_1 !== parm_type_1) console.log("Parm 1 not correct type");
    if (typeof parm_val_2 !== parm_type_2) console.log("Parm 2 not correct type");
    return parm_val_1;
}

The Number before the calling function returns a Number type regardless of the type of the actual value returned, as seen in the 2nd call where typeof rc = number but the value is NaN

the console.log for the above is:

1st call
typeof rc: number   rc: 1
2nd call
Parm 1 not correct type
typeof rc: number   rc: NaN

Upvotes: 0

colxi
colxi

Reputation: 8670

You can implement a system that handles the type checks automatically, using a wrapper in your function.

With this approach, you can build a complete declarative type check system that will manage for you the type checks . If you are interested in taking a more in depth look at this concept, check the Functyped library

The following implementation illustrates the main idea, in a simplistic, but operative way :

/*
 * checkType() : Test the type of the value. If succeds return true, 
 * if fails, throw an Error
 */
function checkType(value,type, i){
  // perform the appropiate test to the passed 
  // value according to the provided type
  switch(type){
    case Boolean : 
      if(typeof value === 'boolean') return true;
      break;
    case String : 
      if(typeof value === 'string') return true;
      break;
    case Number : 
      if(typeof value === 'number') return true;
      break;
    default :
      throw new Error(`TypeError : Unknown type provided in argument ${i+1}`);
  }
  // test didn't succeed , throw error
  throw new Error(`TypeError : Expecting a ${type.name} in argument ${i+1}`);
}


/*
 * typedFunction() : Constructor that returns a wrapper
 * to handle each function call, performing automatic 
 * arguments type checking
 */
function typedFunction( parameterTypes, func ){
  // types definitions and function parameters 
  // count must match
  if(parameterTypes.length !== func.length) throw new Error(`Function has ${func.length} arguments, but type definition has ${parameterTypes.length}`);
  // return the wrapper...
  return function(...args){
    // provided arguments count must match types
    // definitions count
    if(parameterTypes.length !== args.length) throw new Error(`Function expects ${func.length} arguments, instead ${args.length} found.`);
    // iterate each argument value, and perform a
    // type check against it, using the type definitions
    // provided in the construction stage
    for(let i=0; i<args.length;i++) checkType( args[i], parameterTypes[i] , i)
    // if no error has been thrown, type check succeed
    // execute function!
    return func(...args);
  }
}

// Play time! 
// Declare a function that expects 2 Numbers
let myFunc = typedFunction( [ Number, Number ],  (a,b)=>{
  return a+b;
});

// call the function, with an invalid second argument
myFunc(123, '456')
// ERROR! Uncaught Error: TypeError : Expecting a Number in argument 2

Upvotes: 14

fider
fider

Reputation: 2036

Use typeof or instanceof:

const assert = require('assert');

function myFunction(Date myDate, String myString)
{
    assert( typeof(myString) === 'string',  'Error message about incorrect arg type');
    assert( myDate instanceof Date,         'Error message about incorrect arg type');
}

Upvotes: 1

Renaud
Renaud

Reputation: 16491

Check out the new Flow library from Facebook, "a static type checker, designed to find type errors in JavaScript programs"

Definition:

/* @flow */
function foo(x: string, y: number): string {
  return x.length * y;
}
foo('Hello', 42);

Type checking:

$> flow
hello.js:3:10,21: number
This type is incompatible with
  hello.js:2:37,42: string

And here is how to run it.

Upvotes: 27

zVictor
zVictor

Reputation: 3717

It can easilly be done with ArgueJS:

function myFunction ()
{
  arguments = __({myDate: Date, myString: String});
  // do stuff
};

Upvotes: 5

VNO
VNO

Reputation: 3695

No, instead you would need to do something like this depending on your needs:

function myFunction(myDate, myString) {
  if(arguments.length > 1 && typeof(Date.parse(myDate)) == "number" && typeof(myString) == "string") {
    //Code here
  }
}

Upvotes: 12

Related Questions