mapped types syntax and intro
https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gRgKygXigbwFBW1AZge3wC4oBnYAJwEsA7AcwBosc4BDCkmgVwFs4IKGAL4YMoSFACSAEwg1gVUAB4AKgD4U6ZtgDaAaSi0oAawgh8uKCoC6JFfuvDR46AGVWPaKhlyFy+AhqGAD0wTgAegD8omLg0AAKFPg8VKRUuCCqGqiYOFD6hjQmZhZWtlAAFACUKBqJyakQqg5BIrES9SlpuFQQ0pqdqemZAUGhEdEYsgDGADbs0NP4NORQFGDTJIPdvdKiSyvAeFzAXBReaxsAdGwU1SFheU-PUFGiuCdnEFfAABZyFQqADdWLMasgNLkcCDZtooOM3kIqkA
a tweet that prompted the idea for the talk
https://twitter.com/kentcdodds/status/1608187990215655424
but since this isn't possible with satisfies (it doesn't participate in inference, in an example like that it only provides contextual types) the answer was to use a function with a reverse mapped type
https://twitter.com/ecyrbedev/status/1608211211425923073
code from the tweet:
https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwGcpsDEBPAeQCMArcDAOSgFsQAeAFQD4AKAKHjwctAFzwA3gMHwA2gGl4WfAGsQZHIngcAumJ6oWIMXICU8ALxd4ANxxZgAbikBfPibG37TvkRLlqdJhMrDySglB6BqxmlhJSglEgTtLwAPSp8AB6APwuADRSVJGGMVZh0onJ0ulZuYLOBc4m3r5YpJS09MEgofAR8DylEg3wRQND4s7wTU41OXwLQA
this example infers a type like { a: unknown; b: unknown; }
for T
but we can take it further and infer more interesting things instead of uknown
s:
https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwAcYcA3LUANSgmRAB4AVAPgAoAoeeHAIwCsAueAG8OneAG0A0vCz4A1iACeORPAYBdQSLFiS1QQynqANKJ1huglqigBbEAaMBKeAF4m8EjnIBuM-ABfUSCnQS9fNjYiUnIQKhoQFm14KC1-PQhBABYAJlNzS3gWDNoXd2EgnQB6KrEAPQB+YPz4QuTODMEAcigAdxAAZxx7AEIuls4LKxKQMo8hSrEa+qbOANMApz8o4jJKaloklK1PfXhc43gpormKwMu208z4Hv6h0fGrwpZbhcDArZsZaNSJsIA
prisma-like extension example for derived computations (compute
property) with data requirements (needs
property):
https://www.typescriptlang.org/play?ssl=4&ssc=21&pln=4&pc=3#code/CYUwxgNghgTiAEkoGdnwKrJDeBvAUPPAA4CuARhAJZjwB2UAtiAFzzIAuMVdA5oSQrVa0Tm07c+AspRrwovVvVKNy2fAF98+AGak6YDlQD2deCAAeHEHWAAeACoA+ABQCeZDmwJEiAbQBpeB54AGsQAE9jHXgHAF1vAV96EBBgZDYHQLiAbiTfMGNGTyUXCzYABRpQu0xsABowyOjY7PgAMiaomLqYJwBKeABeJ3k6CLzkjUn4LUGfeDgOUhgzOlIICABCPK18QrpOeAth8ysbYBcFnSYqCAiAOSYlBaI6VPTveFEveC5SBAaer5QrFUjWNguUhYGCDEbwaHYAB0P2ByQA9OjkskAHoAfgEQM0-Ty+1MRwip0s1lsVwEek2T2YiWS7zSGTw+TezzY-xAaOxP15MAB+SJyVBJUhiNhw1GAAMACS4GVIhjMDTwZWqn4aeUCoiY7G+fGE4EaEn4IA
the interesting bit here is also that the intersection acts as a "filter": keyof T[K] & keyof User
. By intersecting two unions of strings we end up with members that are available in both unions
bindAll
example, inferring a tuple using the same reverse mapped type technique, the variadic tuple type at the argument position hints TS to infer this a tuple (listeners: [...Bindings<Types>]
):
https://www.typescriptlang.org/play?#code/C4TwDgpgBAQglgOwCaIOYGcA8AVKEAewEy6UAShAIZID2CANiAIIBOLlIm6wLaAfHygBeKAG8AUFCgBtANJREUANYQQNAGZRsAXQBcYyVKihI+7HO0BuQ1PpxuxCC30AKCADczFgJTDB7mjgkaykAX2tw8XEkCABjekoWaHUAVwRY4Dg6KAAjRCQmenpMQ1wCIhIoAAlsAFkAGQBReggAW2JgABpS8AhScuIkUgpqOkZWdk4ABRp0dDgclsb3DuxezBU1TWwBcT4XYETUCGAzTqg7BwQndH1pADpH+GQ0LDXIdD5tb30AoOtxCZoDM5gslisEMB3hBMLJBCJ5ANKgADOgAElEiHUTi0vVCyKgAH5cZAoPprisWAC8shCvQXKJQlBKKQag0AJIIMApYDNNodc7SQwSIzGXr6ABEixSLAl3VFlwqTlcHl8QkEItFeHcNiMAHo9VAAHqE3WheVQc3C3VAyXxOCxJRy3WKxzOKBudxqjW6qQeX1QA3G02iq1hbrfaxAA
a more robust version of the bindAll
that actually maps the inferred string types to their event types ('click'
-> MouseEvent
), with nice autocompletes for those string types:
https://www.typescriptlang.org/play?ts=5.0.0-dev.20230202#code/C4TwDgpgBAqgdgIwJZwCYDE5QLxQBQCUOAfFAG4D2SqA3AFCiSxwDWcFA7nOgK5wDGwJBSy48AOkkBDAE4BzAM4AuKFLggA2gF0i2UmpD1G0AJJwAZhBkBRMhDjAAKuAgAeR49lyIwUrg9ePlAQAB7A9qgKUADedFCqqKi29sAAMkgK4XBWeMYqKJYyUAAKADRQkuJeyqrqBCqU1PTxUonJDumZ9jl5UAVWJQBM5ZXVKgb15FS0dAC+cVAA-CULKtl2MkYuUGaF7cCuCwHyPqVHzkyhWZFQmTIocnR+OxZW+xdux96+wWERUR4XAtlvtVlAAAYiAAk0UBkFm4N+1yiLAgIAo5igXx8wJKsikAFsfFYFK5rGEZFJBO5PCdgBpIXAYXCIAitOV4GxONw+IJhHBiMQNAAGLRg-ZbJidLJWADyCAAVhBqY59kj-lB9s9YvEABZqVAAGwg+1yuoyKml3Rk8qVKq15QgdgcKlVzuAk0aM1mkugVuyMhpgWA6rQUXewfKbpSoZuWpwCwAPlB-XLFcqDtGHMQkzEFvF4maLVjad9HWRXftPdNmgWfQxtgAhFCoB6HeLYkNXDURuk4TXu0unc7bbth27Ae5wOT9u4PJ79nVQXosmhQBaGjIymSWrfWoN08q7N7ug9lksuQW1ihgIQiBSLFQICgUY1qKDJgCCbXdqZtt-5BR6HrBsmGbNAHlJc5g1jcNB0jEdICiMcbgAJQgVoRENEBPxkSkQFcOdpxzbUFg0ABpPosFRdFMRZBQtBUJd4hXD4FAorRa3iTcugDXdeKsM9ThePZT07KM2I4qAADIJynOQr3zKAbzvOAHyfF83ywL8fxSP9ZQA+9a3rEDUGVQ1ZGgcxeVUqBkDQT9DUNdsSxglC4JSIdgDODs2NgqB0MwuBsNw-CXMLY8bEHFwhJ+WSiLkIhkzwBKZJiWYCAWEi8AWYBg1dLyfLsltIJUDRKnA1tp1JcSLyQ4h2QWBR9RkCBUAM1T1LszSMO0qBvySX89wDDrALoSZ4HsjA4HoOgpscw08GiWZVCiAAJRwAFlUkbHhgGAERrGNIkHHKDQFmY5cXBUAAifhN34FgbqK7jhqsFQ8CdFJdFIZaXqgAB6AGCxB+IAD1FgWWYisu3obpo1BuWepSeO3D6vocH70v+oHQZBiGobOHRmiAA
that code can be found in https://github.com/alexreardon/bind-event-listener , the autocomplete will only work in 5.0 though since it depends on this PR: microsoft/TypeScript#51770
createMachine
example: no nesting, just providing autocompletes and types for the transitions targets (play with the strings in NEXT: ""
) + initial
property also depends on the available states
. The interesting bit is also that we can use keyof T
outside of the mapped type
https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzDigxAFkowALLVEAHgBUA+ACjD0SwHMAueAbwBQ8eDSzYoEPgGsQATxyJ4DANzD4AZwzEQGvkJEiA2gGlR+WQqUMAuvvWH4eAPx8ASuBwxgdLTBpcADTwlorKTGqOAL6R8DGCUQCUfABuOFjAaoKEIDrkVDQgLAbm4liSfABEUJWB6lo6egIO8FD2jiJ47R0iAHIAogAaDFW1LSJRddFThgBG3Y5dzT19QyPwlWM9ky07jmALhkslPQPDozPTu5fwwIedqPeGZ+ubNxM3e3F1SWpAA
createMachine
with recursive structure, transition targets only allow strings from the given level of nesting, the same is true about initial
. State<T[K]> & ...
allows this to be recursive - T[K]
is something that TS just tries to figure out and when it recursively goes into State
type alias then it sees a mapped type there again and gathers inferences for it in the same way. The inferred T
here becomes: { a: unknown; b: { nested: unknown; anotherNested: unknown; }; }
https://www.typescriptlang.org/play?#code/C4TwDgpgBAysCGwIB4AqA+KBeKBvAUFFAJYB2xwx8ANgPwBcUA1hCAPYBmUqA3IVAGcESAQzz8iAbQDSJUs1aduAXUZxEKVDOWYAZOKKGobUmIBKEAMZsATgBNkQm2QDmAGgXsuGPkYC+vlAB+MH4dlbU8DbQHACupJaUJlCW0RoAsvCWABZkmugAFNakHMQuasL5AJSMvPj4qRAZWbmkEAUERGQUVNSMAET9bvxCGgKMnYbwExKGJjNGRgByAKIAGqgDQ7NEfsP++4YARguL3ZQ0A21CEHbbi8akpw+rG1uHBzuCleMGD1DXJB2Z7-eZ-f6GVArGCbKCDD7-PZfXYIozwUhsYDZCA2JYQG7A8Ggp5EiFQmFXfFA+4QoKowxIh6MhmHJF+Kp8IA
we can go even further with createMachine
and the upcoming const
type parameters:
https://www.staging-typescript.org/play?ts=5.0.0-pr-51865-14#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEdEigHEJgBlYOYNFlwFMUCAA8BAOwAmqKAG8oqPgNQAuKBQBGAKwgBjYFAC+UAPyEiAIhX8012lE1SIANwgAnJi3DQAkqgAcu5eQvhQRJhOEtJyka4enk6WAIzOUAAMPqzQnMD+clgANBz8ABZeEDJikhCy8iqeAJZSAOYRiV7CUIEhSeGx9fGpjFAWZcCVntVjGdbNMta1cfJklDSYc5b5hRjwyIL5vPYYONil7BVVNQA+UAAGAMQAJArEC0tOAGTKwC3tEwPbBzTS7IoHFAYY6qQTnS7XGYybA5PxQACCEXBGAUc0WmmscGsTHGdjUmlx43G2jgUlpFLmVKg+Kg1kqEHIxMZphJVNpFCmXgZTPGLOsclQMjgkq5TJMvPljBMKOYuSgJwEAGEKFIqM02ug5pgNWgVsN5AAlQwUTwydBNVptUom7W6-U9K0GG12h3tZ2w116g20kDYC5GvZmhp-AEdAi+tqMHqU5kycyaBO81rNYDNRDpqDraiEE3yX6ZuZktAFlPjIgAaWZUkL5GLxthqFomhdOqD6BhpywpYbtAuhD2KvGivGOoLnu99v+jtKmEj9weADo3kXNqWoOWl4DgUqfDJDAg4DMoFQkFIjM0dVADDN7ABZOAGcqtCDoL1SFSEFG8Q9m6BoDmoQiXFwexCGGAAUcx-kGmhbAAlJobgUIszDPhAb4fl+rhwSm2a5ogBJEsUeIyJoADk-KCp4hS0VRpIdsKfIcUypF5ggBJUBQFDWKxIpVhoijckyAkUFxIrcTRUC0QA7hQSkAPpnhAYBqYstGSVSJgiXKRmmCJhlzDqskAKIwHRelTkqqFMEAA
What is mind-bending about this example is that our type parameter depends on itself (!):
const T extends StateConfig<GetStates<T>, GetIds<T>>
We are able to "crawl" T
with GetIds
, gather those available state IDs and provide that as one of the part of the available transition targets. Those IDs become available "globally" within this machine.
We don't exactly have to rely on const
type parameters. We could do this even today by introducing an extra TIds
type parameter:
https://www.staging-typescript.org/play?noErrorTruncation=true&ts=5.0.0-dev.20230220#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEdEigHEJgBlYOYNFlwFMUCAA8BAOwAmqKAG8oqPgNQAuKBQBGAKwgBjYFAC+UAPyEiAIhX8012lE1SIANwgAnJi3DQAkqgAcu5eQvhQRJhOEtJyka4enk6WAIzOUAAMPqzQnMD+clgANBz8ABZeEDJikhCy8iqeAJZSAOYRiV7CUIEhSeGx9fGpjFAWZcCVntVjGdbNMta1cfJklDSYc5b5hRjwyIL5vPYYONil7BVVNQA+UAAGAMQAJArEC0tOAGTKwC3tEwPbBzTS7IoHFAYY6qQTnS7XGYybA5PxQACCEXBGAUc0WmmscGsTHGdjUmlx43G2jgUlpFLmVKg+Kg1kqEHIxMZphJVNpFCmXgZTPGLOsclQMjgkq5TJMvPljBMKOYuSgQQo-ikVDCOAiUVoURWw3ktJAE0yGS6yVVaJOAgAwhRtc02ug5ph7Whih69saGn8AR0CE1Wm1GD1KcyZOZNJg9rzWs1gM1ELGoOtqIQvfJfqH2ryyWh01HxkQANLMqQZ8hZz2w1C0TRep0ut0w05YHMV2gXQh7FXjRXjZ3pgBKhgonhk6HzbVKDze8bkQKg9weADo3pnNjmoHn-mGgSDFYwZIYEHAZlAqEgpEZms6oAYZvYALJwAzlVoQEr9+JDAGc7YAAFHMBjOlQrrNrCrZQW6mClMuqAggAlJobgUIszAvhA76ft+rggVGSYpogBJEj6ooyJoADkb4AJq9AAIrRVF-Kcwp8lxTIsrR6JBAA8pgAASACiY4APr+KxpQAPRyVAUzNI05QUEgCA1No0CIAgFAAO7VM4YEilWyapggBJUBQFDWOxQ7sSY7HOjxUBiTAdG0XMJhKqhTBAA
As long as type parameter is constrained to a primitive type (string
in this example), TS infers unions of literal types - so we can infer 'a' | 'b'
based on all occurences in the argument, instead of just a string
.
There are some problems here though
id: 'ANOTHER_ID'
isn't allowed in within states
. It turns out that, currently, TS doesn't infer to other type parameters within the mapped type... but with this PR it becomes capable of that: microsoft/TypeScript#52737 , we can even verify it with a playground that uses this PR:
https://www.staging-typescript.org/play?ts=5.0.0-pr-52737-7#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEdEigHEJgBlYOYNFlwFMUCAA8BAOwAmqKAG8oqPgNQAuKBQBGAKwgBjYFAC+UAPyEiAIhX8012lE1SIANwgAnJi3DQAkqgAcu5eQvhQRJhOEtJyka4enk6WAIzOUAAMPqzQnMD+clgANBz8ABZeEDJikhCy8iqeAJZSAOYRiV7CUIEhSeGx9fGpjFAWZcCVntVjGdbNMta1cfJklDSYc5b5hRjwyIL5vPYYONil7BVVNQA+UAAGAMQAJArEC0tOAGTKwC3tEwPbBzTS7IoHFAYY6qQTnS7XGYybA5PxQACCEXBGAUc0WmmscGsTHGdjUmlx43G2jgUlpFLmVKg+Kg1kqEHIxMZphJVNpFCmXgZTPGLOsclQMjgkq5TJMvPljBMKOYuSgQQo-ikVDCOAiUVoURWw3ktJAE0yGS6yVVaJOAgAwhRtc02ug5ph7Whih69saGn8AR0CE1Wm1GD1KcyZOZNJg9rzWs1gM1ELGoOtqIQvfJfqH2ryyWh01HxkQANLMqQZ8hZz2w1C0TRep0ut0w05YHMV2gXQh7FXjRXjZ3pgBKhgonhk6HzbVKzze8bkQKg9weADo3pnNjmoHn-mGgSDFYwZIYEHAZlAqEgpEZms6oAYZvYALJwAzlVoQEr9+JDAGc7YAAFHMBjOlQrrNrCrZQW6mClMuqAggAlJobgUIszAvhA76ft+rggVGSYpogBJEj6ooyJoADkb4AJq9AAIrRVF-Kcwp8lxTIsrR6JBAA8pgAASACiY4APr+KxpQAPRyVAtI1FMzTyGpSkIAgFAAO7VAAhNyopSMmqYIASVAUBQ1jsUO7EmOxzo8VAYkwHRTyMSxtFzCYSqoUwQA
- there is still a problem here though. As mentioned, TS gathers inferences for those string literals based on all occurences - it doesn't understand which one is more important and which spots are creating our source of truth. In our example we only want to construct that type from all
id: 'someId'
but we don't want to infer that from on: '#someId'
. The latter should be typed based on the former - constrained to it. In other words, we expect an error on EV: '#INVALID'
:
https://www.staging-typescript.org/play?ts=5.0.0-pr-52737-7&ssl=54&ssc=5&pln=54&pc=19#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEdEigHEJgBlYOYNFlwFMUCAA8BAOwAmqKAG8oqPgNQAuKBQBGAKwgBjYFAC+UAPyEiAIhX8012lE1SIANwgAnJi3DQAkqgAcu5eQvhQRJhOEtJyka4enk6WAIzOUAAMPqzQnMD+clgANBz8ABZeEDJikhCy8iqeAJZSAOYRiV7CUIEhSeGx9fGpjFAWZcCVntVjGdbNMta1cfJklDSYc5b5hRjwyIL5vPYYONil7BVVNQA+UAAGAMQAJArEC0tOAGTKwC3tEwPbBzTS7IoHFAYY6qQTnS7XGYybA5PxQACCEXBGAUc0WmmscGsTHGdjUmlx43G2jgUlpFLmVKg+Kg1kqEHIxMZphJVNpFCmXgZTPGLOsclQMjgkq5TJMvPljBMKOYuSgQQo-ikVDCOAiUVoURWw3ktJAE0yGS6yVVaJOAgAwhRtc02ug5ph7Whih69saGn8AR0CE1Wm1GD1KcyZOZNJg9rzWs1gM1ELGoOtqIQvfJfqH2ryyWh01HxkQANLMqQZ8hZz2w1C0TRep0ut0w05YHMV2gXQh7FXjRXjZ3pgBKhgonhk6HzbVKzze8bkQKg9weADo3pnNjmoHn-mGgSDFYwZIYEHAZlAqEgpEZms6oAYZvYALJwAzlVoQEr9+JDAGc7YAAFHMBjOlQrrNrCrZQW6mClMuqAggAlJobgUIszAvhA76ft+rggVGSYpogBJEj6ooyJoADkb4AJq9AAIrRVF-Kcwp8lxTIsrR6JBAA8pgAASACiY4APr+KxpQAPRyVAtI1FMzTyGpSkIAgFAAO7VAAhNyopSMmqYIASVAUBQ1jsUO7EmOxzo8VAClQAAAsAqAALQSJARg+Z4nhTtyYkwHRTz+EEMDogAMjJtFzCYSqoUwQA
And with a small help of NoInfer
magic we can achieve that:
https://www.staging-typescript.org/play?ts=5.0.0-pr-52737-7#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEdEigHEJgBlYOYNFlwFMUCAA8BAOwAmqKAG8oqPgNQAuKBQBGAKwgBjYFAC+UAPyEiAIhX8012lE1SIANwgAnJi3DQAkqgAcu5eQvhQRJhOEtJyka4enk6WAIzOUAAMPqzQnMD+clgANBz8ABZeEDJikhCy8iqeAJZSAOYRiV7CUIEhSeGx9fGpjFAWZcCVntVjGdbNMta1cfJklDSYc5b5hRjwyIL5vPYYONil7BVVNQA+UAAGAMQAJArEC0tOAGTKwC3tEwPbBzTS7IoHFAYY6qQTnS7XGYybA5PxQACCEXBGAUc0WmmscGsTHGdjUmlx43G2jgUlpFLmVKg+Kg1kqEHIxMZphJVNpFCmXgZTPGLOsclQMjgkq5TJMvPljBMKOYuSgQQo-ikVDCOAiUVoURWw3ktJAE0yGS6yVVaJOAgAwhRtc02ug5ph7Whih69saGn8AR0CE1Wm1GD1KcyZOZNJg9rzWs1gM1ELGoOtqIQvfJfqH2ryyWh01HxkQANLMqQZ8hZz2w1C0TRep0ut0w05YHMV2gXQh7FXjRXjZ3pgBKhgonhk6HzbVKzzeGq1Os8WAHQKg9weADo3pnNjmoHn-mGgSDFYwZIYEHAZlAqEgpEZms6oAYZvYALJwAzlVoQCU-bxEMAZztgAAUcwGM6VCus2sKtnBbqYKU8ZyCCACUmhuBQizMB+EDfr+-6uBBUZJimiAEkSPqijImgAORfgAmr0AAijF0X8pzCnyfFMiyjHokEADymAABIAKJjgA+v4nGlAA9EpUC0jUUzNPIWlqQgCAUAA7tUACE3KilIyapggBJUBQFDWNxQ7cSY3HOgJUAqVAAACwCoAAtBIkBGAFnieFO3JSTATFPP4QQwOiAAyCmMXMJhKphTBAA
At the moment it's only possible to infer into T[K]
, it has to stay "naked". I believe though that this can be improved further and I created a proposal that would allow us to infer into T[K]['data']
, T[K]['result']
, etc. I call this inferring to "concrete index types":
microsoft/TypeScript#51612
I strongly believe that this new capability would allow some libraries to ditch such monstrosities:
https://github.com/TanStack/query/blob/17816d6eedc7450fd4c6fcdfa2fad87272327c2a/packages/react-query/src/useQueries.ts#L34
for something much simpler, like here (this is just illustrative and doesn't cover for everything that the real thing actually covers at the moment):
https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgIoFdoE8Bi6QJjAD2IAwqZAB5gA8AUMsgCobYDSEWyENEIAEwDOyIWCigA5gBp6APmQBvRsgCOmKFk5YAXCzabt9AL716YLAAcUBrAHlLRUiIC8SlUwDWXPWIkhJD2QBODA4AH49fE8QYgB3EABuIJgQABFQiKiQGPiklVNzKxQAVSEIA2AIIQcnECFaZh4+QREAJQg4AVIAGywAQSgoOCxaW1qSerkFN2UmAG12ZFBkbyxiGBYAXT05pjUNLR8WRa35gHI1862g9WwcEEjkAAogpkIqPVs8AjqKcF4dGYpwuVy2clk+2QAEpkC4FAAFKDEAC2wHKjRB51SGTC1wUAB8Tuwztj0plrkFyj0IIQns8QmE9MCSRccRStrD4cTSYy4JSmKZCgJaT04FAUDB8IRJsh0OVKtUGEwmoD+MJkB0ur0BkMRmNDhNnHJ5M87hJqnp5gA6W1lCoaKo1RyTBrMOScvQAN2IwAEySAA