ตั้งค่าประเภทสำหรับพารามิเตอร์ฟังก์ชันหรือไม่


161

มีวิธีให้ฟังก์ชั่นจาวาสคริปต์รู้หรือไม่ว่าพารามิเตอร์บางตัวเป็นประเภทที่แน่นอน?

ความสามารถในการทำสิ่งนี้จะสมบูรณ์แบบ:

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

ขอบคุณ!

อัปเดต : เนื่องจากคำตอบนั้นดังกึกก้องว่า "ไม่" ถ้าฉันต้องการmyDateได้รับการปฏิบัติเหมือนเป็นวันที่ (เพื่อที่จะเรียกใช้ฟังก์ชั่นวันที่ในนั้น) ฉันต้องโยนมันเป็นวันที่ภายในฟังก์ชั่นหรือตั้งตัวแปรใหม่ของ พิมพ์วันที่ไปหรือไม่


1
ไม่ได้อยู่ในความรู้สึกในตัวและโดยทั่วไป คุณสามารถทำสิ่งนี้ด้วยตัวเอง แต่แล้วมันก็ขึ้นอยู่กับว่าคุณให้คำจำกัดความ "ประเภทใดประเภทหนึ่ง"
hugomg

2
นอกจากนี้ยังมีการเรียนใน JavaScript ไม่มีดังนั้นจึงไม่มีเพียงDate object
กำจัด


@dmr นั่นไม่ใช่คลาส Dateเป็นฟังก์ชั่น ลองดูที่stackoverflow.com/questions/1646698/…เพื่อค้นหาเพิ่มเติมเกี่ยวกับnewคำหลักJavaScript นอกจากนี้เนื่องจากไม่มีชั้นเรียนไม่มีการคัดเลือกนักแสดง คุณสามารถเรียกฟังก์ชั่นที่คุณต้องการ หากวัตถุมีพวกเขาพวกเขาจะทำงานมิฉะนั้นคุณจะได้รับข้อผิดพลาด
กำจัด

2
มันเป็นรุ่นเก่า แต่ไม่มีใครพูดถึงตัวพิมพ์ดีด
kit

คำตอบ:


180

ไม่จาวาสคริปต์ไม่ใช่ภาษาที่พิมพ์แบบคงที่ บางครั้งคุณอาจต้องตรวจสอบพารามิเตอร์ด้วยตนเองในส่วนของฟังก์ชัน


180
พรและคำสาป
เจฟฟรีย์สวีนีย์

40
@JeffreySweeney ไม่ได้พิมพ์ PHP แบบคงที่ แต่คุณมีตัวเลือกที่จะพิมพ์คำใบ้ใน php คุณเคยดูแอปพลิเคชันส่วนใหญ่ของ nodejs หรือไม่? แต่ละฟังก์ชันมีอาร์กิวเมนต์และคุณไม่มีเงื่อนงำว่าแต่ละอาร์กิวเมนต์คืออะไร เรากำลังพูดถึงการโต้แย้งนับพันและเมื่ออ่านคุณต้องอ่านรหัสทั้งหมดและรหัสทั้งหมดของผู้โทรและผู้โทร ฯลฯ พร? แน่นอนคุณจะต้องล้อเล่น
Toskan

14
นอกเหนือจากการทุบตีใครบางคนที่เรียกว่าไม่มีคุณสมบัติที่อนุญาตให้พิมพ์คำอวยพรฉันอาจต้องการชี้ให้เห็น typescript: typescriptlang.orgโดยทั่วไป EM6 + การบอกกล่าวประเภท
Toskan

23
@JeffreySweeney มันไม่ใช่คำอวยพร มันเป็นมะเร็ง
Robo Robok

1
@Toskan ฉันจะไม่บอกว่ามันไม่ได้เป็นพระพร ฉันใช้จาวาสคริปต์มาสี่ปีแล้วและนี่เป็นเพียงลักษณะของบางภาษา ชุดของภาษาการเขียนโปรแกรมควรอยู่ในช่วงตั้งแต่การพิมพ์แบบอ่อนไปจนถึงการพิมพ์อย่างรุนแรงเช่นเดียวกับที่ควรมีตั้งแต่ระดับต่ำถึงระดับสูง นอกจากนี้ JavaScript ยังมีคำหลักinstanceofและtypeofคำหลักเพื่อช่วยในเรื่องนี้ แม้ว่านี่จะใช้รหัสมากขึ้น แต่ก็มีนักพัฒนาที่เลือก JavaScript เป็นภาษาสำหรับบางสิ่งที่ขึ้นอยู่กับประเภท สำหรับแบ็กเอนด์ของแอพพลิเคชั่นขนาดใหญ่ nodejs? ฉันคิดว่ามันควรจะเป็นสามัญสำนึก
Marvin

82

ไม่ได้อยู่ใน javascript ด้วยตนเอง แต่ใช้โหมดขั้นสูงของ Google Closure Compiler คุณสามารถทำได้:

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

ดูhttp://code.google.com/closure/compiler/docs/js-for-compiler.html


1
นี้ยังทำงานร่วมกับ / ช่วยคราสจาวาแก้ไข - มุมมองเค้าร่างและรหัสเสร็จ ในขณะที่foo( /*MyType*/ param )วิธีที่อธิบายไว้ที่นี่ยังใช้งานได้: stackoverflow.com/a/31420719/1915920
Andreas Dietrich

ฉันรู้ว่าคำถามนี้มีอายุเท่าไร แต่ฉันต้องการชี้ให้เห็นว่ามันเป็นเกียรติแก่ IntelliJ คำตอบที่ไม่ปรับปรุงมากที่นี่
ChettDM

67

ขณะที่คุณไม่สามารถแจ้ง JavaScript ภาษาที่เกี่ยวกับประเภทคุณสามารถแจ้ง IDE ของคุณเกี่ยวกับพวกเขาเพื่อให้คุณได้รับการเติมข้อความอัตโนมัติที่มีประโยชน์อื่น ๆ อีกมากมาย

นี่คือสองวิธีในการทำเช่นนั้น:

  1. ใช้JSDocระบบสำหรับการบันทึกโค้ด JavaScript ในความคิดเห็น โดยเฉพาะอย่างยิ่งคุณจะต้องมี@paramคำสั่ง :

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

    คุณยังสามารถใช้ JSDoc เพื่อกำหนดประเภทที่กำหนดเองและระบุประเภทใน@paramคำสั่งได้ แต่โปรดทราบว่า JSDoc จะไม่ทำการตรวจสอบประเภทใด ๆ มันเป็นเพียงเครื่องมือเอกสาร ในการตรวจสอบประเภทที่กำหนดไว้ใน JSDoc มองเข้าไปในtypescriptซึ่งสามารถแยกแท็ก JSDoc

  2. ใช้การบอกประเภทโดยการระบุชนิดที่ถูกต้องก่อนพารามิเตอร์ใน a
    /* comment */:

    คำใบ้ประเภท JavaScript ใน WebStorm

    นี่เป็นเทคนิคที่แพร่หลายอย่างมากที่ใช้โดย ReactJSเป็นต้น มีประโยชน์มากสำหรับพารามิเตอร์ของการโทรกลับที่ส่งไปยังห้องสมุดบุคคลที่สาม

สิ่งที่พิมพ์ด้วยพิมพ์ดีด

สำหรับการตรวจสอบประเภทจริงทางออกที่ใกล้เคียงที่สุดคือการใช้ TypeScript, a ( ส่วนใหญ่ ) superset ของ JavaScript นี่คือtypescript ใน 5 นาที


8
ทำอย่างไรจึงจะได้สิ่งนี้ต่อไปVSCode?
Anand Undavia

2
ขอบคุณ แม้ว่าสิ่งนี้จะขึ้นอยู่กับ IDE ฉันใช้ VI และไม่ทำงาน
negrotico19

@ negrotico19: viเป็นตัวแก้ไขที่ถูกทารุณกรรมไม่ใช่ IDE คุณสามารถทำมากของสิ่งที่อยู่ในviเช่นเดียวกับที่คุณสามารถทำมิวสิควิดีโอใน Excel ความคิดที่ดี? อาจจะไม่. ใช้เครื่องมือที่เหมาะสมสำหรับงาน
Dan Dascalescu

23

ตรวจสอบFlow library ใหม่จาก Facebook "ตัวตรวจสอบชนิดคงที่ออกแบบมาเพื่อค้นหาข้อผิดพลาดประเภทในโปรแกรม JavaScript"

ความหมาย:

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

การตรวจสอบประเภท:

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

และนี่คือวิธีการที่จะใช้มัน


วิธีเพิ่มคำจำกัดความประเภทถ้า x เป็นประเภทวันที่ ie foo (x: Date): string {} นี่เป็นวิธีที่ถูกต้องหรือไม่
Aakash Sigdel

12

ไม่ แต่คุณจะต้องทำสิ่งนี้ขึ้นอยู่กับความต้องการของคุณ:

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

12

คุณสามารถใช้ระบบที่จัดการการตรวจสอบประเภทโดยอัตโนมัติโดยใช้ wrapper ในฟังก์ชั่นของคุณ

ด้วยวิธีนี้คุณสามารถสร้างความสมบูรณ์declarative type check systemที่จะจัดการให้คุณตรวจสอบประเภท หากคุณสนใจที่จะมองลึกลงไปในแนวคิดนี้มากขึ้นให้ตรวจสอบไลบรารี Functyped

การใช้งานต่อไปนี้แสดงให้เห็นถึงแนวคิดหลักในวิธีที่ง่าย แต่ทำงานได้ :

/*
 * 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


11

แก้ไข: เจ็ดปีต่อมาคำตอบนี้ยังได้รับ upvotes เป็นครั้งคราว ไม่เป็นไรถ้าคุณกำลังมองหาการตรวจสอบรันไทม์ แต่ตอนนี้ฉันอยากจะแนะนำการตรวจสอบเวลาแบบคอมไพล์โดยใช้ Typescript หรือ Flow ดูhttps://stackoverflow.com/a/31420719/610585ด้านบนสำหรับข้อมูลเพิ่มเติม

คำตอบเดิม:

มันไม่ได้สร้างไว้ในภาษา แต่คุณสามารถทำได้ด้วยตัวคุณเองอย่างง่ายดาย คำตอบของ Vibhu คือสิ่งที่ฉันจะพิจารณาวิธีการตรวจสอบชนิดทั่วไปใน Javascript หากคุณต้องการสิ่งที่เป็นรูปธรรมมากขึ้นลองทำดังนี้: (แค่ตัวอย่างเพื่อให้คุณเริ่มต้น)

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

5

สามารถทำได้อย่างง่ายดายด้วยArgueJS :

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

2
ดูเหมือนห้องสมุดที่ดี ยินดีด้วย.
FRD

1

ใช้typeofหรือ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');
}

0

บางทีฟังก์ชั่นผู้ช่วยเช่นนี้ แต่ถ้าคุณเห็นว่าคุณใช้ไวยากรณ์เช่นนี้เป็นประจำคุณควรเปลี่ยนไปใช้ 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
}

0

ฉันก็คิดถึงเรื่องนี้เช่นกัน จากพื้นหลัง C คุณสามารถจำลองชนิดโค้ดส่งคืนฟังก์ชันรวมถึงชนิดพารามิเตอร์โดยใช้สิ่งต่อไปนี้:

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

Number ก่อนฟังก์ชั่นการโทรจะส่งคืนชนิด Number โดยไม่คำนึงถึงชนิดของค่าจริงที่ส่งคืนตามที่เห็นในการโทรครั้งที่ 2 โดยที่ typeof rc = number แต่ค่าเป็น NaN

console.log สำหรับด้านบนคือ:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.