Closed spiffytech closed 5 years ago
Very good question. There probably needs to be better documentation here.
The heart of the issue is that your get
function is of type string | number => number
but your mod
function is of type
(f: (v: string | number) => string | number) => string | number => string | number)
Which isn't consistent with a mod
function's signature. A mod
function for a lens of type A
to B
should have signature:
(f: (v: B) => B) => A => A
Or in English: "Take a type-preserving function over the focus type of the lens (number
in this case), and return a type-preserving function for the source type (string | number
here)"
So it should be:
(f: (v: number) => number) => string | number => string | number)
amount
is a string | number
and fn
is a number => number
. You need to convert amount
from a string | number
into a number
before you call fn
(and normally you would then need to convert the result of fn
back to a string | number
afterwards, but number
is already a subtype of string | number
)
So a more correct lens would look like:
const toNum = (amt: string | number): number => parseFloat(amt.toString())
const amountAsNumber = {
get: toNum,
mod: fn => amount => fn(toNum(amount)),
}
Getting the types to fully typecheck has some gotcha's as of right now (I'm fixing them before v2)
First import the Lens
and mark the type:
import {Lens} from 'shades/types/utils';
...
const amountAsNumber: Lens<string | number, number> = {
...
}
However, this will still yell at you saying that your lens is missing a traversal
property. This is a boolean value that states whether you want it to apply as a traversal
or not. For this, we don't, so we add:
const amountAsNumber: Lens<string | number, number> = {
...
traversal: false
}
And now everything should typecheck!
Putting it all together:
import {Lens} from 'shades/types/utils';
const toNum = (amt: string | number) => parseFloat(amt.toString())
const idAsNumber: Lens<string | number, number> = {
get: toNum,
mod: fn => amount => fn(toNum(amount)),
traversal: false
};
There is more information on creating and typing Virtual Lenses here
Excellent, works perfectly. Thanks for the thorough explanation!
P.S. This has been fixed in the new beta
If you do:
npm install shades@beta
The new code would just be:
import {Lens} from 'shades';
const toNum = (amt: string | number) => parseFloat(amt.toString())
const idAsNumber: Lens<string | number, number> = {
get: toNum,
mod: fn => amount => fn(toNum(amount))
};
I'm trying to create a virtual lens that casts a
string | number
to anumber
. It works in the Node REPL, but gives TypeScript errors.The
shades.get
line has the following error:Argument of type '"amount"' is not assignable to parameter of type 'Lens<{}, any>'.