3

Say I have the following four objects:

const foo1: Readonly<{foo: number, bar: ?string}> = {
   foo: 123
}

const foo1: Readonly<{foo: number, bar: ?string}> = {
   foo: 123,
   bar: '1123'
}


const bar1: Readonly<{foo2: number, bar2: ?string}> = {
   foo2: 123
}

const foo1: Readonly<{foo: number, bar: ?string}> = {
   foo2: 123,
   bar2: '1123'
}

I now want to convert them into fp-ts Option bearing objects of the form:

{foo: number, bar: Option<string>}

The naive approach is to write two functions (or to indeed do those computations inline):

const makeOptional1 = (obj: Readonly<{foo: number, bar: ?string}>) => {
  return (obj.bar === undefined) ? none : some(obj[key]
}

const makeOptional2 = (obj: Readonly<{foo2: number, bar2: ?string}>) => {
  return (obj.bar2 === undefined) ? none : some(obj[key]
}

But say, that I wanted to extract a more abstracted function, one which could take an arbitrary object and arbitrary key, and return the correct Option.

A first attempt might look something like so:

const optionalKey = <T, D>(val: D, key: string): Option<T> => {
  if (val[key] === undefined) {
    return none
  } else {
    return some(val[key])
  }
}

This fails to compile with:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'unknown'.
  No index signature with a parameter of type 'string' was found on type 'unknown'.ts(7053)

The error message makes perfect sense but I don't know how to fix it. Any ideas would be welcome

Matthias
  • 13,607
  • 9
  • 44
  • 60
Abraham P
  • 15,029
  • 13
  • 58
  • 126

1 Answers1

3

please take a look: TS playground

import { Option, some, none } from 'fp-ts/lib/Option';


const foo1: Readonly<{ foo: number, bar?: string }> = {
  foo: 123
}

const foo2: Readonly<{ foo: number, bar?: string }> = {
  foo: 123,
  bar: '1123'
}


const bar1: Readonly<{ foo2: number, bar2?: string }> = {
  foo2: 123
}

const bar2: Readonly<{ foo2: number, bar2?: string }> = {
  foo2: 123,
  bar2: '1123'
}

const makeOptional1 = <T extends Readonly<{ foo: number, bar?: string }>, K extends keyof T>(obj: T, key: K) => {
  return (obj.bar === undefined) ? none : some(obj[key])
}

const makeOptional2 = <T extends Readonly<{ foo2: number, bar2?: string }>, K extends keyof T>(obj: T, key: K) => {
  return (obj.bar2 === undefined) ? none : some(obj[key])
}

type Values<T> = T[keyof T];



const optionalKey = <T, K extends keyof T>(val: T, key: K): Option<Values<T>> => val[key] ? some(val[key]) : none;

const result = optionalKey({ age: 1 }, 'age'); // Option<number>

fp-ts version:2.8.6