ColinE
ColinE

Reputation: 70160

Adapt a function in TypeScript, with type checking

I'd like to adapt a number of existing functions, with differing signatures, yet maintain the type / signature checking of the original.

For example:

const functionOne = (name: string, age: number) => ({
    name,
    age
})

const functionTwo = (name: string, sex: string) =>  ({
    name,
    sex
})

const addIndexToReturn = (adaptee: any, index: number) =>
  (...args: any[]) => 
    Object.assign(adaptee(...args), { index })

const functionWithIndex = addIndexToReturn(functionOne, 22)

// I don't get any type checking here
console.log(functionWithIndex('Frank', 32))

As you can see, I have a number of functions that return various different objects. I have a function addIndexToReturn that adapts these, returning a new function that adds an index to their returned value.

However, the new function doesn't have any type checking on its parameters.

Any ideas?

Upvotes: 1

Views: 353

Answers (2)

Chris
Chris

Reputation: 522

This seems to work using the trick from LINQ -

interface Adapter {
    <A, T>(adaptee: (a: A) => T): (a: A) => T;
    <A, B, T>(adaptee: (a: A, b: B) => T): (a: A, b: B) => T; 
}

const fn = (x: String): String => x + y.toString();
const fn2 = (x: String, y: Number): String => x + y.toString();

const adapt: Adapter = (adaptee) => adaptee;    

adapt(fn)(1)
adapt(fn)("")
adapt(fn2)(1, 2)
adapt(fn2)("", 2)

Upvotes: 1

Paarth
Paarth

Reputation: 10397

Basically you're throwing away the type information of the parameter function because your return lambda takes in any[]

You could attempt to handle the issue with something like this:

type Indexed<T> = T & { index: number };
function addIndexToReturn<T, V>(func: (param: T) => V, index: number) : (param:T) => Indexed<V> {
    return (x:T) => {
        let result = func(x) as Indexed<V>;
        result.index = index;
        return result;
    }
}

But this only works for single-parameter functions. I don't believe you can express this correctly for function with arbitrary values for reasons similar to my other recent answer.

If we are to handle the case of variable length parameters, we must treat the parameters as a Collection. Because all variables must have a single (though possibly parameterized) type so too must this collection object. However, the types of the various functions do not align. (param:A) => B is not the same type as (param:B) => C and cannot be stored in the same well typed container (barring union types but those won't scale either).

Upvotes: 1

Related Questions