Search code examples
typescriptformsjavascript-objectstypescript-generics

Typing Object.fromEntries(new FormData(form))


TypeScript newbie here, having problems with Object.fromEntries. I'm trying to pares form and cast its values to something else. For example, given homogeneous shape of all data being strings, below works:


// example form content:
// {
//    foo: 'bar',
//    bar: 'biz'
//  }
//  return values
// {
//    foo: 'bar',
//    bar: 'biz'
//  }

export const getFormData = (form: HTMLFormElement) => {
  return Object.fromEntries<string>(
    new FormData(form) as Iterable<[PropertyKey, string]>,
  )
}

How I can cast values returned from the above to values others than strings? For example:


// example form content:
// {
//    foo: '1',
//    bar: 'biz',
//    biz: 'false'
//  }
//  desired return values
// {
//    foo: 1,
//    bar: 'biz',
//    biz: false
//  }

export const getFormData = (form: HTMLFormElement) => {
  return Object.fromEntries<string>(
    new FormData(form) as Iterable<[PropertyKey, string]>,
  )
}

My feeling is that I can somehow fix it with generics and interfaces but I'm at a loss how to proceed. If someone could ELI5 I would be grateful.

EDIT: my tsconfig.json:

{
    "compilerOptions": {
        "moduleResolution": "node",
        "module": "es2020",
        "lib": ["esnext", "dom", "dom.iterable"],
        "target": "es2020",
        "downlevelIteration": true,
        "importsNotUsedAsValues": "error",
        "preserveValueImports": true,
        "isolatedModules": true,
        "resolveJsonModule": true,
        "sourceMap": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "baseUrl": ".",
        "allowJs": true,
        "checkJs": true,
    }
}

EDIT2: Better explanation of the desired outcome.


Solution

  • In your TS config add "dom.iterable" to lib:

    "lib": [
      "es2019",
      "dom",
      "dom.iterable"
    ],
    

    Now that TS knows that the DOM element can return an .entries() iterator, you don't need to type it explicitly anymore:

    Object.fromEntries(new FormData(form))
    

    Since you're using an HTML form, you won't get anything but strings and blobs. If you want to convert some of the strings to numbers, casting won't help you. Just iterate the entries, and convert values that fit a certain criteria - for example a certain key, or if converting them to numbers doesn't produce NaN. For example (TS Playground):

    const formData = new FormData();
    
    formData.append('name', 'max');
    formData.append('age', '123456');
    
    const result = Object.fromEntries([...formData].map(([key, value]) => [
      key,
      key === 'age' ? +value : value
    ]))
    
    console.log(result)