mike35x95x1
mike35x95x1

Reputation: 225

Is it possible to use type in a function called from parent function in Typescript?

I want to call a function inside another function and use its type parameter as "default" type when calling child function.

Is it possible in Typescript?

// Parent interface
interface IParent {
    id: number;
    name: string;
}

// Child interface with foreign key
interface IChild {
    id: number;
    name: string;
    ParentId: number;
}

// Parent function declaration:
function select<T>(query: {
    select: string,
    join: string,
}) {
    return `${query.select} ${query.join}`;
}

// Child function declaration (F type is optional):
function buildJoin<T, F = Record<any, any>>(foreignKey: keyof T, otherColumn: keyof F): string {
    return 'JOIN f on t.foreignKey = f.otherColumn';
}

// Strong typing:
select<IParent>({
    select: 'select * from Parent',
    join: buildJoin<IChild, IParent>('ParentId', 'id'), // explicitly typed "ParentType"
});

// Requested behaviour:
select<IParent>({
    select: 'select * from Parent',
    join: buildJoin<IChild>('ParentId', 'id'), // if 2nd type parameter omitted it should be taken from parent function
});

Typescript playground link

Upvotes: 1

Views: 216

Answers (3)

geoffrey
geoffrey

Reputation: 2474

There is another option relying on tagged primitive types, currying and function composition.

const select = <Parent>(select: string) =>
    select as string & { _parent: Parent };

const join = <Child>(foreignKey: keyof Child) => <Parent>(otherColumn: keyof Parent) =>
    (select: string & { _parent: Parent }) =>
        `${select}\nJOIN f on t.foreignKey = f.otherColumn`;

You can use it like so

import { pipe } from 'fp-ts/lib/function'

const ok1 = pipe(
    select <IParent>("select * from Parent"),
    join <IChild>("ParentId") ("id")
)

const ok2 = pipe(
    select <IParent>("select * from Parent"),
    join <IChild>("ParentId") <IParent>("id")
)

plyaground

Upvotes: 1

rohit
rohit

Reputation: 1

yes it is possible. By calling the super() method in the constructor method, we call the parent's constructor method and gets access to the parent's properties and methods. Inheritance is useful for code reusability: reuse properties and methods of an existing class when you create a new class.

Upvotes: -1

T.J. Crowder
T.J. Crowder

Reputation: 1075755

You're trying to infer a type argument to buildJoin based on where it's called, which you can't do. So in that sense, the answer to your question is "no."

As an alternative, you might write a fluent interface in which select returns an object with a join method, which can then inherit the type parameter's value. Here's a rough example (not intended to be an actual robust implementation [though it does work for this limited example], just an example):

// Parent interface
interface IParent {
    id: number;
    name: string;
}

// Child interface with foreign key
interface IChild {
    id: number;
    name: string;
    ParentId: number;
}

// Parent function declaration:
function select<ParentType>(select: string) {
    return {
        query: select,
        join<ChildType>(foreignKey: keyof ChildType, otherColumn: keyof ParentType) {
            this.query += "\nJOIN f on t.foreignKey = f.otherColumn";
            return this;
        },
        build() {
            return this.query;
        },
    };
}

// Strong typing:
const sql = select<IParent>("select * from Parent")
    .join<IChild>("ParentId", "id")
    .build();

Playground example

Upvotes: 1

Related Questions