i had go at making an F# equivalent of the following Typescript types:
/**
* styling variables for this App
*/
export type YouTubeCssVariables =
/* html anchor */
'--thumbs-header-link-color' |
'--thumbs-header-link-text-decoration' |
/* thumbs set */
'--thumbs-set-header-color' |
'--thumbs-set-background-color' |
'--thumbs-set-header-position' |
'--thumbs-set-padding-top'
;
/**
* defines a name-value pair for a CSS variable
*/
export interface YouTubeCssOption {
/**
* CSS variable name
*/
variableName: YouTubeCssVariables;
/**
* CSS variable value
*/
variableValue: string;
}
we see that YouTubeCssVariables
is a literal type and my assertion is that there is no concept of this kind of type in F# (is this assertion correct?); so, here is my rendition of the F# ‘equivalent’:
type YouTubeCssVariable =
| ThumbsHeaderLinkColor of string
| ThumbsHeaderLinkTextDecoration of string
| ThumbsSetHeaderColor of string
| ThumbsSetBackgroundColor of string
| ThumbsSetHeaderPosition of string
| ThumbsSetPaddingTop of string
member this.Name =
match this with
| ThumbsHeaderLinkColor _ -> "--thumbs-header-link-color"
| ThumbsHeaderLinkTextDecoration _ -> "--thumbs-header-link-text-decoration"
| ThumbsSetHeaderColor _ -> "--thumbs-set-header-color"
| ThumbsSetBackgroundColor _ -> "--thumbs-set-background-color"
| ThumbsSetHeaderPosition _ -> "--thumbs-set-header-position"
| ThumbsSetPaddingTop _ -> "--thumbs-set-padding-top"
member this.Value =
match this with
| ThumbsHeaderLinkColor v -> v
| ThumbsHeaderLinkTextDecoration v -> v
| ThumbsSetHeaderColor v -> v
| ThumbsSetBackgroundColor v -> v
| ThumbsSetHeaderPosition v -> v
| ThumbsSetPaddingTop v -> v
member this.Pair =
let n = this.Name
let v = this.Value
n, v
is this the right direction? …when, yes, then are these match
expressions too verbose? …is there some more terse syntax out there i am missing?
CodePudding user response:
What's really your goal here? Surely it's not making F# types more like Typescript types. That article strikes me as trying to make Javascript more like F#. I suggest forgetting about Typescript here and just use F# types to best solve the problems you have.
If your payload is always a string (which it seems to be) I would consider splitting that type into two:
open System
type CssVariableType =
| ThumbsHeaderLinkColor
| ThumbsHeaderLinkTextDecoration
| ThumbsSetHeaderColor
| ThumbsSetBackgroundColor
| ThumbsSetHeaderPosition
| ThumbsSetPaddingTop
type CssVariable = {
Type: CssVariableType
Value: String
}
I would drop the Name, Value, and Pair type additions and just write a module containing the functions you need. Pattern match as needed inside the functions. Maybe a print function would look like this:
module CssVariable =
let print var =
let name =
match var.Type with
| ThumbsHeaderLinkColor -> "--thumbs-header-link-color"
| ThumbsHeaderLinkTextDecoration -> "--thumbs-header-link-text-decoration"
| ThumbsSetHeaderColor -> "--thumbs-set-header-color"
| ThumbsSetBackgroundColor -> "--thumbs-set-background-color"
| ThumbsSetHeaderPosition -> "--thumbs-set-header-position"
| ThumbsSetPaddingTop -> "--thumbs-set-padding-top"
$"{name}={var.Value}"
Example:
let v = { Type = ThumbsHeaderLinkColor; Value = "blue" }
CssVariable.print v // "--thumbs-header-link-color=blue"
CodePudding user response:
I think the main reason why TypeScript has type definitions with string values as cases is to allow type checking of popular programming patterns in JavaScript. If you do not need to type-check code where "magic strings" are used to represent cases of a union, then it makes much more sense to use an explicit union type like the one used in F#.
The question then really becomes about assigning a nice textual representation to each case of a union, perhaps because they represent some CSS classes that also can be represented as strings. I think the solution with just having a format
function that takes the union value and returns a corresponding string is perfectly reasonable - you only have to write this once (and it is also more future proof in case some names change).
If you needed this for a large number of union cases, it may make sense to use some reflection magic to generate the CSS names from your F# DU case names automatically. I do not think it is worthe the extra complexity for a few cases, but it can be done. Something like the following would work (but it does not handle possible errors):
type CssVariableType =
| ThumbsHeaderLinkColor
| ThumbsHeaderLinkTextDecoration
| ThumbsSetHeaderColor
| ThumbsSetBackgroundColor
| ThumbsSetHeaderPosition
| ThumbsSetPaddingTop
open Microsoft.FSharp.Reflection
let formatCase<'T> () =
let formatName (s:string) =
[ yield "-"
for c in s do
if System.Char.IsUpper(c) then yield "-"
yield c.ToString().ToLower() ]
|> String.concat ""
let lookup =
FSharpType.GetUnionCases(typeof<'T>)
|> Seq.map (fun c -> FSharpValue.MakeUnion(c, [||]), formatName c.Name)
|> dict
fun (c:'T) -> lookup.[c]
let f = formatCase<CssVariableType>()
f ThumbsHeaderLinkColor // "--thumbs-header-link-color"
f ThumbsSetHeaderColor // "--thumbs-set-header-color"