Skip to content

'Discarding Parameters' plus 'Duck Typing' makes every parameter optional? #9352

Closed
@SlurpTheo

Description

@SlurpTheo

After reading the Comparing two functions section of the handbook I got myself pretty confused and concerned. I have an example below that I think (taken to the prepare-for-duck-typing-extreme) means I should code every class's method as if all of its parameters are optional (even though they're not defined as such).

TypeScript Version:

1.8.10 / next (1.9.0-dev.20160624-1.0)

Code

(function () { "use strict";


class C1 { one(...hi: string[]) { } }
class C2 { one(hi: string) { hi.length; } }

function Test() {
    const c2 = new C2();
    const c2as1: C1 = c2;   // BAD assignment

    // TypeError: Cannot read property 'length' of undefined
    c2as1.one();            // BAD execution

    // error TS2346: Supplied parameters do not match any signature of call target.
    //c2.one();

    // error TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'.
    //                 Happens once you enable TypeScript 2.0's --strictNullChecks.
    //c2as1.one(void 0);
    //c2.one(void 0);
}


})();

Actual behavior:

I have to remember to write run-time checking of typeof hi === "string" ? hi.length : 0 instead of the simple/easy hi.length inside my C2#one(hi: string) method to prevent against a TypeError.

Expected behavior:

I'd love for there to be some way for TypeScript to stop the BAD assignment line above; maybe one that turns off 'discarding' parameters altogether?

The defense given for discarding parameters is a signature like Array#forEach -- where callers commonly omit the 2nd/3rd parameters to the callbackfn. That doesn't make sense to me though, why isn't the *.d.ts for Array#forEach just defined as:

forEach(callbackfn: (value?: T, index?: number, array?: T[]) => void, thisArg?: any): void;

Instead of how it is?

forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;

Maybe my targeting of parameter discarding is too strong, but it really seems like there should be some way to configure TypeScript to block the assertion of a C2 instance to a C1 interface without requiring me to just write better code like:

class betterC1 { one(hi: string, ...restHi: string[]) { } }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions