File: conditionalTypeDoesntSpinForever.ts

package info (click to toggle)
node-typescript 3.3.3333-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 324,548 kB
  • sloc: makefile: 6; sh: 3
file content (119 lines) | stat: -rw-r--r-- 5,783 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// @target: es6
// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc --target es6` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.

export enum PubSubRecordIsStoredInRedisAsA {
    redisHash = "redisHash",
    jsonEncodedRedisString = "jsonEncodedRedisString"
  }
  
  export interface PubSubRecord<NAME extends string, RECORD, IDENTIFIER extends Partial<RECORD>> {
    name: NAME;
    record: RECORD;
    identifier: IDENTIFIER;
    storedAs: PubSubRecordIsStoredInRedisAsA;
    maxMsToWaitBeforePublishing: number;
  }
  
  type NameFieldConstructor<SO_FAR> =
    SO_FAR extends {name: any} ? {} : {
      name: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {name: TYPE}>
    }
  
  const buildNameFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
    "name" in soFar ? {} : {
      name: <TYPE>(instance: TYPE = undefined) =>
        buildPubSubRecordType(Object.assign({}, soFar, {name: instance as TYPE}) as SO_FAR & {name: TYPE}) as BuildPubSubRecordType<SO_FAR & {name: TYPE}>
    }
  );
  
  type StoredAsConstructor<SO_FAR> =
    SO_FAR extends {storedAs: any} ? {} : {
      storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}>;
      storedRedisHash: () => BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}>;
    }
  
  const buildStoredAsConstructor = <SO_FAR>(soFar: SO_FAR) => (
    "storedAs" in soFar ? {} : {
      storedAsJsonEncodedRedisString: () =>
        buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) as
          BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}>,
      storedAsRedisHash: () =>
        buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) as
          BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}>,
    }
  );
  
  type IdentifierFieldConstructor<SO_FAR> =
    SO_FAR extends {identifier: any} ? {} :
    SO_FAR extends {record: any} ? {
      identifier: <TYPE extends Partial<SO_FAR["record"]>>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
    } : {}
  
  const buildIdentifierFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
    "identifier" in soFar || (!("record" in soFar)) ? {} : {
      identifier: <TYPE>(instance: TYPE = undefined) =>
        buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) as BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
    }
  );
  
  type RecordFieldConstructor<SO_FAR> =
    SO_FAR extends {record: any} ? {} : {
      record: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {record: TYPE}>
    }
  
  const buildRecordFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
    "record" in soFar ? {} : {
      record: <TYPE>(instance: TYPE = undefined) =>
        buildPubSubRecordType(Object.assign({}, soFar, {record: instance as TYPE}) as SO_FAR & {record: TYPE}) as BuildPubSubRecordType<SO_FAR & {record: TYPE}>
    }
  );
  
  type MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> =
    SO_FAR extends {maxMsToWaitBeforePublishing: any} ? {} : {
      maxMsToWaitBeforePublishing: (t: number) => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
      neverDelayPublishing: () => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
    }
  
  const buildMaxMsToWaitBeforePublishingFieldConstructor = <SO_FAR>(soFar: SO_FAR): MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> => (
    "maxMsToWaitBeforePublishing" in soFar ? {} : {
      maxMsToWaitBeforePublishing: (instance: number = 0) =>
        buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: instance})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
      neverDelayPublishing: () =>
        buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
    }
  ) as MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>;
  
  type TypeConstructor<SO_FAR> =
    SO_FAR extends {identifier: any, record: any, maxMsToWaitBeforePublishing: number, storedAs: PubSubRecordIsStoredInRedisAsA} ? {
      type: SO_FAR,
      fields: Set<keyof SO_FAR>,
      hasField: (fieldName: string | number | symbol) => fieldName is keyof SO_FAR
    } : {}
  
  const buildType = <SO_FAR>(soFar: SO_FAR) => (
    "identifier" in soFar && "object" in soFar && "maxMsToWaitBeforePublishing" in soFar && "PubSubRecordIsStoredInRedisAsA" in soFar ? {} : {
      type: soFar,
      fields: () => new Set(Object.keys(soFar) as (keyof SO_FAR)[]),
      hasField: (fieldName: string | number | symbol) => fieldName in soFar
    }
  );
  
  type BuildPubSubRecordType<SO_FAR> =
    NameFieldConstructor<SO_FAR> &
    IdentifierFieldConstructor<SO_FAR> &
    RecordFieldConstructor<SO_FAR> &
    StoredAsConstructor<SO_FAR> & // infinite loop goes away when you comment out this line
    MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> &
    TypeConstructor<SO_FAR>
  
  const buildPubSubRecordType = <SO_FAR>(soFar: SO_FAR) => Object.assign(
    {},
    buildNameFieldConstructor(soFar),
    buildIdentifierFieldConstructor(soFar),
    buildRecordFieldConstructor(soFar),
    buildStoredAsConstructor(soFar),
    buildMaxMsToWaitBeforePublishingFieldConstructor(soFar),
    buildType(soFar)
  ) as BuildPubSubRecordType<SO_FAR>;
  const PubSubRecordType = buildPubSubRecordType({});