typespec icon indicating copy to clipboard operation
typespec copied to clipboard

Ability to specify Map/Record key types/formats/patterns

Open laduke opened this issue 1 year ago • 4 comments

I had asked for advice in this discussion #1626 and got pointed to creating an issue.

If you use

alias MyMap = Record<int32>;

You can't say anything about the keys. They are just string as far as I can tell.

I wanted to be able to put something like @pattern("[a-f0-9]{10}") on my keys. Could also imagine: @format("uuid")

Hope that is detailed enough.

laduke avatar Jan 26 '24 22:01 laduke

@markcowl: look for existing issue

markcowl avatar Jan 29 '24 19:01 markcowl

Things to think about if we want to provide similar functionality to TypeScript here. If we were to have the key be a string template we could use it so the type system knows what are the values for those types of keys and combine that with other constraint.

Example of what I mean in typescript https://www.typescriptlang.org/play?#code/C4TwDgpgBASgcgVwLZQLywgYwPYCcAmAPAAYAeAtAHbLkAkA3gM7C4CWlA5gL7EA0U1JACMIuAHwBuALAAoUJFgBlFuw5oMOAiQrNcdJis49+u1ZNmz50AGLZs6+MigAyJYY7SZsnJWZQAhgBcULb26PSyUFFQAEQUguT+McEAjLyR0XFUNELJsbox6TLRsTos5Jh5MQVFJVm65Ph5ACxFXEA

Example use case we could see in TypeSpec

(I used Record<K, V> to show the example but this is most likely not going to be the type as this would be significantly breaking.)

alias C1 = Record<string, string>; // every property is a string
alias C2 = Record<string, int32>; // every property is a int32


model C3 is Record<string, int64> { // all properties are int64 but name is int32  - works because int32 is subtype of int64
  name: int32
}

alias C4 = Record<string, int64> & {name: int32}; // all properties are int64 but name is int32 - works because int32 is subtype of int64

model C5  { // All properties are int64 but name is string
  name: string;
  ...Record<string, int64>
}

model C5  { // name is string and can have extra properties of type int64 or boolean
  name: string;
  ...Record<string, int64>
  ...Record<string, boolean>
}

// Here we are saying that all starting with int64- are int64 and all starting with boolean- are boolean
alias C6 = Record<"int64-${string}", int64> & Record<"boolean-${string}", boolean> & {name: string};


model C7 { // name is a string then there is extra properties that start with int64-(int64) and boolean-(boolean
  name: string;
  ...Record<"int64-${string}", int64>;
  ....Record<"boolean-${string}", boolean>
}
model C8 { // name is a string then there is extra properties that start with int64-(int64 or object) and boolean-(boolean
  name: string;
  ...Record<"int64-${string}", int64>;
  ...Record<"int64-${string}", object>;
  ....Record<"boolean-${string}", boolean>
}

timotheeguerin avatar Feb 15 '24 16:02 timotheeguerin

I think it might be possible to do it in a non breaking maner

https://typespec.io/playground?c=QGluZGV4ZXIoSywgVikKbW9kZWwgUmVjb3JkS2V5ZWQ8xBggPSBLPiB7fQrHIFRlc3QgewogIGxlZ2FjeTE6zTJzdHJpbmc%2BO8kgMs4gaW50MzLFH25ld9U8LCDLRG5ld89ByCXNSTPOJCJ4LSR7xil9IsorfQo%3D&e=%40typespec%2Fopenapi3&options=%7B%7D

it just means we can't have a constraint valueof string on the key and this might need to be done manually in the decorator.

timotheeguerin avatar Feb 23 '24 19:02 timotheeguerin

This doesn't cover custom types so bring back a Map type would be better.

@mapConfig(K, V)
scalar Map<K, V>;

timotheeguerin avatar Mar 13 '24 17:03 timotheeguerin

Sefaria uses a pattern where the responses to some bulk queries are records, where the objects are keyed by the query parameters. (Here is a simplified Playground example.) In such a case, it would be really nice to be able to specify that the Record is keyed by the type of the source parameter.

I think it's acceptable to require the key type to be a valueof string - after all, For JSON, that's what's supported. (One could argue it's TypeScript's job to support serialization of fancy models, not TypeSpec's.)

cc @markcowl

Arithmomaniac avatar Sep 22 '24 07:09 Arithmomaniac