Generic Constraints

Coming from TypeScript, I was wondering if there was a way to guarantee that a generic should include certain data with something like a generic constraint.

function foo<T extends {a: string}>(x: T) {
    console.log(x.a)   
}

// good
foo({a: '', b: '3'})

// error
foo({b: '3'})

Rescript allows you do that thanks to the structural typing of objects:

let f = x => Js.log(x["a"])

As @tsnobip said, you’d need structural typing for that.

To get a sense for type signatures, here’s an example that allows only values that at least have a {"a": string} within an object type:

let foo = (x: {.."a": string}) => {
  let str = x["a"] ++ " world"
  Js.log(str)
}

foo({"a": "hello", "b": "test"})
foo({"a": "hello", "c": "test"})

// This has type: {"b": string}
//  Somewhere wanted: {.."a": string}
foo({"b": "test"})

Playground Link

Unfortunately subtyping is not documented yet. {.. denotes an “open structural object type”.

If you’d want to pull out the type definition for your parameter, it would look a little different:

type mySuperType<'a> = {.."a": string} as 'a

This declaration tells the compiler that this type should at least contain "a": string within its definition. We need the 'a placeholder for subtypes:

type sub = mySuperType<{"a": string, "b": string}>

let a: sub = {"a": "test", "b": "test"}

Here, we make a concrete subtype that requires you to provide the "a": string, otherwise the compiler would error out.

7 Likes

this is great, thank you!