3

I have the following type of object on my client side (typescript):

interface Example {
    id: number;
    name: string;
    createdOnDate: Date;
}

I'm fetching data from the server that matches the type (same property names):

async function fetchExamples(): Promise<Example[]> {
    const response = await fetch("/example/index");

    if (!response.ok) {
        throw Error("...");
    }

    return (await response.json()) as Example[];
}

The problem is that, the createdOnDate is returned from the server as a string in an ISO format (such as 2019-01-01T14:30:39.0000000Z for example). As you can guess, the createdOnDate on the client doesn't get parsed to a Date, but it remains a string.

Is there a way to cast it automatically without iterating through all of the response data. I have the following, which works as expected:

function parseDateFromServer(date: string): Date {
    if (date) {
        return typeof date === "string" ? new Date(date) : date;
    }

    return null;
}

async function fetchExamples(): Promise<Example[]> {
    const response = await fetch("/example/index");

    if (!response.ok) {
        throw Error("...");
    }

    const data = await response.json();

    const parsedData: Example[] = data.map(
        (x) =>
            ({
                ...x,
                createdOnDate: parseDateFromServer(x.createdOnDate)
            } as Example)
    );

    return parsedData;
}

I was wondering if I can do it in a more "elegant" way (also I have this code throughout my codebase, I'd rather not go and change it everywhere and my real types are quite more complex than this example one).

0

1 Answer 1

4

How often are you using the createdDate? You could leave it as a string.

interface Example {
    id: number;
    name: string;
    createdOnDate: string;
}

And just convert it to a Date when you need to manipulate it.

First solution I could think of was/is similar to your outlined solution above (e.g. construct an Example class on server response). I'll cast a vote and see if anyone has other options.

EDIT: here is what class construction could do. At least it allows you to put the construction bits in its own file/location to keep your async calls clean and tidy. (https://stackblitz.com/edit/typescript-1npw4f)

interface IExample {
  id: number;
  name: string;
  createdOnDate: string;
}

export class Example {
  id: number;
  name: string;
  createdOnDate: Date;

  constructor(config: IExample) {
    this.createdOnDate = config && config.createdOnDate
    && typeof config.createdOnDate === "string"
      ? new Date(config.createdOnDate) 
      : config.createdOnDate as unknown as Date
  }
}

const _examples: IExample[] = [
  {
    id: 1, name: 'Hulk', createdOnDate: new Date().toISOString()
  },
  {
    id: 2, name: 'Smash', createdOnDate: new Date().toISOString()
  },
];

const EXAMPLES: Example[] = _examples.map(e => new Example(e));
console.log(EXAMPLES); 
console.log(typeof EXAMPLES[0].createdOnDate) // object

This way, in your async calls, you can just map over results and build a new class. i.e.

async function fetchExamples(): Promise<Example[]> {
    const response = await fetch("/example/index");

    if (!response.ok) {
        throw Error("...");
    }

    const data = await response.json();

    const parsedData: Example[] = data.map(x => new Example(x));

    return parsedData;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Well that's an example type (just to illustrate the scenario, however there are many more dates - modified, created, lastAccessed etc., and many more entities). Also, I use the dates often since I'm doing ordering/re-ordering on the client UI. Thanks for the response, mate.
sorry I couldn't of been more help. I'll edit my answer to show a class construction pattern, it is not as verbose as your example. maybe you can pick and choose and start to build up classes.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.