Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I enforce the last argument of a function in typescript?

Tags:

typescript

Let's say I have a function type interface that I want to use to enforce a certain signature of particular functions.

Is there a way to enforce that the last argument of a function must be a particular type?

Is there a way to do something like this:

interface SomeType { /*...*/ }

/**
 * this is the function type I want to enforce
 * I want the last argument of any `Modifcation` to be `SomeType`
 */
interface Modification {
    (...args: [...firstArgs: any[], lastArgument: SomeType]): void
}

// enforcing `Modification`
const someModification: Modification = (firstParam: string, second: number, third: SomeType) => {
    /*...*/
}

I know that typescript utilizes use the spread syntax and tuple types but the psuedo-code above doesn't work.

Is there a way to do this?


Edit:

Disclaimer: I don't think there's a way to do what I want and this question may not be completely constructive as a result.

Context:

I have a set of classes and I want to enforce a certain convention on particular methods of those classes. I can enforce that a certain method must conform to a function type by declaring another interface and saying that my class implements that interface.

e.g.

interface Convention {(arg0: string, arg1: number): Something}

interface MyClassMethodRequirements {
    myMethod: Convention,
    myOtherMethod: Convention
}

class MyClass implements MyClassMethodRequirements {
    myMethod(a: string, b: number) { return new Something(); }
    myOtherMethod(label: string, aNumber: number) { return new Something(); }
    otherMethod() { return 'just another non-conforming method' }
}

What I want to achieve:

The Convention above is just an example. What I want to enforce is that a method asserted to be a Convention must have/consider both parameters.

e.g. (pseudo-code)

interface NewConvention {(firstArg: any, lastArg?: RequiredType): Something}

interface MyClassMethodRequirements {
    myMethod: NewConvention,
    myOtherMethod: NewConvention
}

class MyClass implements MyClassMethodRequirements {
    myMethod(a: string, b?: RequiredType) { return new Something(); }
    // i want an error here because `myOtherMethod` doesn't list `lastArg: RequiredType` in it's parameters
    myOtherMethod(label: string) { return new Something(); }
    otherMethod() { return 'just another non-conforming method' }
}

for some more context, the lastArg of NewConvention is suppose to be an optional override.

Again, I don't think what I'm trying to achieve is possible so this may be unconstructive.

Thanks for your help :)

like image 743
Rico Kahler Avatar asked Oct 18 '22 11:10

Rico Kahler


2 Answers

If you know the maximum number of parameters, you could do as many overloads as needed.

type Modification =
    { (arg1: any, lastArgument: SomeType): void } |
    { (arg1: any, arg2: any, lastArgument: SomeType): void } |
    { (arg1: any, arg2: any, arg3: any, lastArgument: SomeType): void };
like image 198
Rodris Avatar answered Oct 21 '22 06:10

Rodris


You can have the first argument (of type SomeType) as optional like so:

interface SomeType {
    a: number; // just so it's not an empty definition
}

type Modification =
    { (first: SomeType, ...rest: (string | number)[]): void; }
    | { (...rest: (string | number)[]): void; }

// fine
const someModification1: Modification = (firstParam: string, second: number) => {}
const someModification2: Modification = (third: SomeType, firstParam: string, second: number) => { }

// error
const someModification3: Modification = (firstParam: string, second: number, third: SomeType) => { }

Notice that if you use any[] for the args then that will cover the SomeType arg as well.

like image 35
Nitzan Tomer Avatar answered Oct 21 '22 08:10

Nitzan Tomer