1+ export { }
2+
3+ /* Here is one thing we could do. In Java, we could just create a
4+ new class for each, probably a record with only one argument.
5+ Then its nominal type system would see the error. In TypeScript,
6+ we need to force its structural type system to see the two kinds
7+ of IDs as different somehow.
8+
9+ Enter: "branding".
10+ */
11+
12+ type StudentId = ( string & { readonly _StudentId : unique symbol } )
13+ interface Student {
14+ id : StudentId ,
15+ studentName : string ,
16+ concentration : string
17+ }
18+
19+ type CourseId = ( string & { readonly _CourseId : unique symbol } )
20+ interface Course {
21+ id : CourseId ,
22+ name : string ,
23+ enrollment : Set < Student >
24+ }
25+
26+ /** The registrar maps student IDs to the number of credits taken. */
27+ type RegistrarCredits = { [ id : StudentId ] : number }
28+ let registrar : RegistrarCredits = { }
29+ /** ...and is told to give credit by invoking this function (which
30+ in reality would return a promise...) */
31+ function registrarGiveCredit ( studentId : StudentId ) {
32+ registrar [ studentId ] = registrar [ studentId ] + 1
33+ }
34+
35+ /** The course is over, and, to keep the example simple all students
36+ * enrolled passed. Great! Let's give them credit. */
37+ function courseFinished ( c : Course ) {
38+ c . enrollment . forEach ( ( s : Student ) => {
39+ console . log ( `Congratulations ${ s } .` )
40+ registrarGiveCredit ( c . id ) // ERROR! Huzzah
41+ } ) }
42+
43+
44+
45+ /////////////////////////////////////////////////////////////////////
46+
47+
48+
49+ // Whenever we do some type gymnastics, we want to make sure we have
50+ // not made the new type impossible to instantiate!
51+ const nimTelson : Student = {
52+ id : '123' ,
53+ studentName : "Nim Telson" ,
54+ concentration : "undeclared"
55+ }
56+ // Uh oh. This shouldn't be surprising: there's no brand in '123'.
57+ // But we also don't want to spend actual memory storing the field...
58+ function makeStudentId ( id : string ) : StudentId {
59+ // Yes! A typecast! But invoking this function "blesses" the use
60+ // of this string as a StudentId
61+ return id as StudentId
62+ }
63+ const nimTelson2 : Student = {
64+ id : makeStudentId ( '123' ) , // not a "constructor" really.
65+ studentName : "Nim Telson" ,
66+ concentration : "undeclared"
67+ }
0 commit comments