#How would I type a composition of functions like this

33 messages · Page 1 of 1 (latest)

soft orbit
#

I'm writing some wrapper functions in one of my projects (a language parser for a programming language) and found it difficult to figure out how to type something I would use in plain JS or other dynamic languages often.. Here's the use case without any typing and comments:

// This function throws if an incorrect type has been passed to it, and if not
// forward to another function:
const guardNumeric = fn => x => {  
  if (x.type !== AST_NODE_TYPES.AST_NUM)
    throw new BlahBlahError(x.type);  
  return fn(x);
};

// The forwarded functions either return the ast node as is, or return the value wrapped in their body:
const identity = x => x;
const unwrap   = x => x.body;

// We make guards for numbers by composing:
const ensureNum  = guardNumeric(identity);
const getNumBody = guardNumeric(unwrap);
// ...There are to be dozen of other types that are wrapped similarly:

I tried all kinds of gymnastics with this but couldn't come up with anything, so I thought if some more knowledgeable TS-wizards would explain/give tips and help me grow?

What I'm aiming at would be a type signature where ensureNum could be of type:
(node: AstNode) => AstNumNode and getNumBody would be (node: AstNode) => number.. in this case the type AstNumNode always is of type that's lke:

type AstNumNode = {
  type: AstNodeType;
  body: number;
};
dull herald
#

your message refers to a type named AstNode. is that a union of AstNumNode with other stuff like AstStringNode/AstBooleanNode/etc? do you have those types defined already? if so could you provide them?

#

is this on the right track?

heady trellisBOT
#
mkantor#0

Preview:```ts
declare class BlahBlahError extends Error {}

enum AstNodeType {
AST_NUM = "AST_NUM",
AST_BOOLEAN = "AST_BOOLEAN",
}
type AstNumNode = {
type: AstNodeType.AST_NUM
body: number
}
type AstBooleanNode = {
type: AstNodeType.AST_BOOLEAN
body: boolea
...```

soft orbit
#

lemme drop you an explanation real quick!

#

it's basically an union yes!

export type AstNode =
  | AstLiteral
  | AstSymNode
  | AstCollection;
#

AstLiteral is Num, String, Boolean subtypes..

#

they basically just say "the body is of this type"

#

AstNumNode it has number.. String has string.. AstNode is just.. "every possible AstNode type ever"

#

the AstNodeType is defined as..

export const AST_NODE_TYPES = {
  // NOTE: Literals:
  AST_NIL: "AST_NIL",
  AST_NUM: "AST_NUM",
  AST_STR: "AST_STR",
  AST_BOOL: "AST_BOOL",
  AST_KW:   "AST_KW",

  // NOTE: Symbols:
  AST_SYM: "AST_SYM",

  // NOTE: Collections:
  AST_LIST:     "AST_LIST",
  AST_VEC:      "AST_VEC",
  AST_HASH_MAP: "AST_HASH_MAP",
} as const;

export type AstNodeType = keyof typeof AST_NODE_TYPES;
#

bascially very much equivalent of Enums yes!

#

I just make it a map like that because I don't want to misstype that string ever 😂

#

kinda dumb but.. just still used to do that way.. then if I want to give the type of that key to TypeScript somewhere I use the literal type..

#

but your idea of it is correct yes!

dull herald
#

looks like i pretty much guessed right, yeah

soft orbit
#

exactomundo!

dull herald
#

here's a tweak to use your union/const setup:

heady trellisBOT
#
mkantor#0

Preview:```ts
declare class BlahBlahError extends Error {}

export const AST_NODE_TYPES = {
// NOTE: Literals:
AST_NIL: "AST_NIL",
AST_NUM: "AST_NUM",
AST_STR: "AST_STR",
AST_BOOL: "AST_BOOL",
AST_KW: "AST_KW",

// NOTE: Symbols:
AST_SYM: "AST_SYM",

// NOTE: Co
...```

soft orbit
#

oh! that'ss nifty!!! COOL

dull herald
#

(fixed a small mistake)

soft orbit
#

I don't know why I missed that but that really makes sense!

#

but great! thanks!

#

this way I can write the validation part once and just have different utility functions return me different things depending if I need to process the low-level contents or just do some prevalidation on the AstNode!

#

oh yeah! just ran back to fix it immediately! thanks a lot! that was fast!

dull herald
#

sure thing

soft orbit
#

!resolved

dull herald
#

@soft orbit if you're interested, you can even make it a bit more general:

heady trellisBOT
#
mkantor#0

Preview:```ts
declare class BlahBlahError extends Error {}

export const AST_NODE_TYPES = {
// NOTE: Literals:
AST_NIL: "AST_NIL",
AST_NUM: "AST_NUM",
AST_STR: "AST_STR",
AST_BOOL: "AST_BOOL",
AST_KW: "AST_KW",

// NOTE: Symbols:
AST_SYM: "AST_SYM",

// NOTE: Co
...```

soft orbit
#

oh I was just about to go and do that as an exercise!

dull herald
#

heh, sorry to spoil it

soft orbit
#

no no!

#

I can just not look at it

#

and just use it as "the correct answer!"