0

I'm trying to create a method like this:

myMethod<T>(obj: Record<string, any>): string[];

The result of the method should be the keys of the object obj that are type Date in the generic T.

In other words, if there is a type and objects like in the following code:

type MyType = {
  foo: string; // to be ignored as it's not Date
  bar: number; // to be ignored as it's not Date
  firstDate: Date;
  secondDate: Date;
}

const myObj1 = {
  p1: 'hello',  // ignored as p1 is not attribute of MyType
  p2: 'world',  // ignored as p1 is not attribute of MyType
  firstDate: '2021-04-12'
};

const myObj2 = {
  p1: 'hello',  // ignored as p1 is not attribute of MyType
  p2: 'world',  // ignored as p1 is not attribute of MyType
  secondDate: '2021-04-12'
};

const myObj3 = {
  firstDate: 123,
  secondDate: true,
};

We should have

const result1 = myMethod<MyType>(myObj1); // ===> result1 = ['firstDate'];
const result2 = myMethod<MyType>(myObj2); // ===> result2 = ['secondDate'];
const result3 = myMethod<MyType>(myObj3); // ===> result3 = ['firstDate', 'secondDate'];

I'd like to know if it's possible in typescript and eventually how to do it.

Raffaele
  • 737
  • 7
  • 21
  • 2
    No, TypeScript's type system is [erased](//www.typescriptlang.org/docs/handbook/2/basic-types.html#erased-types) when emitted to JavaScript; there is no `MyType` at runtime, and your calls will look like `myMethod(myObj2)`. No matter how you implement `myMethod` it will not know about `MyType`, so there's no way to inspect which property keys are assignable to `Date`. You could presumably change it to `myMethod(anActualObjectOfMyType, myObj2)` where the JavaScript implementation can inspect `anActualObjectOfMyType` to see which keys are instances of `Date`; would that approach work for you? – jcalz Oct 29 '21 at 15:37
  • 1
    Like [this code](https://tsplay.dev/w6B06w). Note that there are no generics here; unless you want `result2` to be seen by the compiler as `Array<"secondDate">` instead of `Array`, there's no need for generics. Let me know if you want to see this as an answer. – jcalz Oct 29 '21 at 15:41
  • Thanks @jcalz, unfortunately the solution doesn't fit with my needs, but for me it's enough to know that it's impossible to do it in typescript. – Raffaele Oct 29 '21 at 15:46

1 Answers1

0

With the help of @jcalz answer, I believe this is possible, if you are willing to define the object's type you are passing into myMethod .

Here is a possible solution using TuplifyUnion type:

// taken from https://stackoverflow.com/questions/55127004/how-to-transform-union-type-to-tuple-type/55128956#55128956

// oh boy don't do this
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type LastOf<T> =
  UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) ? R : never

// TS4.0+
type Push<T extends any[], V> = [...T, V];

// TS4.1+ 
type TuplifyUnion<T, L = LastOf<T>, N = [T] extends [never] ? true : false> =
  true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L>




type MyType = {
  foo: string; // to be ignored as it's not Date
  bar: number; // to be ignored as it's not Date
  firstDate: string;
  secondDate: string;
}

declare function myMethod<T extends object, R extends object>(obj: R): TuplifyUnion<Extract<keyof R, keyof T>>;

const myObj1 = {
  p1: 'hello',  // ignored as p1 is not attribute of MyType
  p2: 'world',  // ignored as p1 is not attribute of MyType
  firstDate: '2021-04-12'
}
const result1 = myMethod<MyType, typeof myObj1>(myObj1);

const myObj2 = {
  p1: 'hello',  // ignored as p1 is not attribute of MyType
  p2: 'world',  // ignored as p1 is not attribute of MyType
  secondDate: '2021-04-12'
}
const result2 = myMethod<MyType, typeof myObj2>(myObj2);

const myObj3 = {
 firstDate: 123,
 secondDate: true,
}
const result3 = myMethod<MyType, typeof myObj3>(myObj3);
Pedro Figueiredo
  • 2,304
  • 1
  • 11
  • 18
  • 2
    Your solution link doesn't go anywhere useful (just brings you to https://www.shorturl.at/ homepage). Even if you do have a working link, you should put the actual code as plain text in the answer, you can't rely on external sites to convey information here (but as supplementary info they are great). – jcalz Oct 29 '21 at 19:06
  • You are correct @jcalz, TIL ;) – Pedro Figueiredo Nov 02 '21 at 09:50