@@ -12,32 +12,72 @@ export enum RepositoryPropertyName {
1212}
1313
1414/** Parsed types of the known repository properties. */
15- type AllRepositoryProperties = {
15+ export type AllRepositoryProperties = {
1616 [ RepositoryPropertyName . DISABLE_OVERLAY ] : boolean ;
1717 [ RepositoryPropertyName . EXTRA_QUERIES ] : string ;
1818} ;
1919
2020/** Parsed repository properties. */
2121export type RepositoryProperties = Partial < AllRepositoryProperties > ;
2222
23+ /** Maps known repository properties to the type we expect to get from the API. */
24+ export type RepositoryPropertyApiType = {
25+ [ RepositoryPropertyName . DISABLE_OVERLAY ] : string ;
26+ [ RepositoryPropertyName . EXTRA_QUERIES ] : string ;
27+ } ;
28+
29+ /** The type of functions which take the `value` from the API and try to convert it to the type we want. */
30+ export type PropertyParser < K extends RepositoryPropertyName > = (
31+ name : K ,
32+ value : RepositoryPropertyApiType [ K ] ,
33+ logger : Logger ,
34+ ) => AllRepositoryProperties [ K ] ;
35+
36+ /** Possible types of `value`s we get from the API. */
37+ export type RepositoryPropertyValue = string | string [ ] ;
38+
39+ /** The type of repository property configurations. */
40+ export type PropertyInfo < K extends RepositoryPropertyName > = {
41+ /** A validator which checks that the value received from the API is what we expect. */
42+ validate : (
43+ value : RepositoryPropertyValue ,
44+ ) => value is RepositoryPropertyApiType [ K ] ;
45+ /** A `PropertyParser` for the property. */
46+ parse : PropertyParser < K > ;
47+ } ;
48+
49+ /** Determines whether a value from the API is a string or not. */
50+ function isString ( value : RepositoryPropertyValue ) : value is string {
51+ return typeof value === "string" ;
52+ }
53+
54+ /** A repository property that we expect to contain a string value. */
55+ const stringProperty = {
56+ validate : isString ,
57+ parse : parseStringRepositoryProperty ,
58+ } ;
59+
60+ /** A repository property that we expect to contain a boolean value. */
61+ const booleanProperty = {
62+ // The value from the API should come as a string, which we then parse into a boolean.
63+ validate : isString ,
64+ parse : parseBooleanRepositoryProperty ,
65+ } ;
66+
2367/** Parsers that transform repository properties from the API response into typed values. */
2468const repositoryPropertyParsers : {
25- [ K in RepositoryPropertyName ] : (
26- name : K ,
27- value : string ,
28- logger : Logger ,
29- ) => AllRepositoryProperties [ K ] ;
69+ [ K in RepositoryPropertyName ] : PropertyInfo < K > ;
3070} = {
31- [ RepositoryPropertyName . DISABLE_OVERLAY ] : parseBooleanRepositoryProperty ,
32- [ RepositoryPropertyName . EXTRA_QUERIES ] : parseStringRepositoryProperty ,
71+ [ RepositoryPropertyName . DISABLE_OVERLAY ] : booleanProperty ,
72+ [ RepositoryPropertyName . EXTRA_QUERIES ] : stringProperty ,
3373} ;
3474
3575/**
3676 * A repository property has a name and a value.
3777 */
3878export interface GitHubRepositoryProperty {
3979 property_name : string ;
40- value : string ;
80+ value : RepositoryPropertyValue ;
4181}
4282
4383/**
@@ -85,12 +125,6 @@ export async function loadPropertiesFromApi(
85125 ) ;
86126 }
87127
88- if ( typeof property . value !== "string" ) {
89- throw new Error (
90- `Expected repository property '${ property . property_name } ' to have a string value, but got: ${ JSON . stringify ( property ) } ` ,
91- ) ;
92- }
93-
94128 if ( isKnownPropertyName ( property . property_name ) ) {
95129 setProperty ( properties , property . property_name , property . value , logger ) ;
96130 }
@@ -117,14 +151,30 @@ export async function loadPropertiesFromApi(
117151 }
118152}
119153
120- /** Update the partial set of repository properties with the parsed value of the specified property. */
154+ /**
155+ * Validate that `value` has the correct type for `K` and, if so, update the partial set of repository
156+ * properties with the parsed value of the specified property.
157+ */
121158function setProperty < K extends RepositoryPropertyName > (
122159 properties : RepositoryProperties ,
123160 name : K ,
124- value : string ,
161+ value : RepositoryPropertyValue ,
125162 logger : Logger ,
126163) : void {
127- properties [ name ] = repositoryPropertyParsers [ name ] ( name , value , logger ) ;
164+ const propertyOptions = repositoryPropertyParsers [ name ] ;
165+
166+ // We perform the validation here for two reasons:
167+ // 1. This function is only called if `name` is a property we care about, to avoid throwing
168+ // on unrelated properties that may use representations we do not support.
169+ // 2. The `propertyOptions.validate` function checks that the type of `value` we received from
170+ // the API is what expect and narrows the type accordingly, allowing us to call `parse`.
171+ if ( propertyOptions . validate ( value ) ) {
172+ properties [ name ] = propertyOptions . parse ( name , value , logger ) ;
173+ } else {
174+ throw new Error (
175+ `Unexpected value for repository property '${ name } ' (${ typeof value } ), got: ${ JSON . stringify ( value ) } ` ,
176+ ) ;
177+ }
128178}
129179
130180/** Parse a boolean repository property. */
0 commit comments