createComponent.test-d.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { expectError, expectType } from 'tsd'
  2. import { describe, createComponent, PropType, ref } from './index'
  3. describe('with object props', () => {
  4. interface ExpectedProps {
  5. a?: number | undefined
  6. b: string
  7. bb: string
  8. cc?: string[] | undefined
  9. dd: string[]
  10. ccc?: string[] | undefined
  11. ddd: string[]
  12. }
  13. const MyComponent = createComponent({
  14. props: {
  15. a: Number,
  16. // required should make property non-void
  17. b: {
  18. type: String,
  19. required: true
  20. },
  21. // default value should infer type and make it non-void
  22. bb: {
  23. default: 'hello'
  24. },
  25. // explicit type casting
  26. cc: Array as PropType<string[]>,
  27. // required + type casting
  28. dd: {
  29. type: Array as PropType<string[]>,
  30. required: true
  31. },
  32. // explicit type casting with constructor
  33. ccc: Array as () => string[],
  34. // required + contructor type casting
  35. ddd: {
  36. type: Array as () => string[],
  37. required: true
  38. }
  39. },
  40. setup(props) {
  41. // type assertion. See https://github.com/SamVerschueren/tsd
  42. expectType<ExpectedProps['a']>(props.a)
  43. expectType<ExpectedProps['b']>(props.b)
  44. expectType<ExpectedProps['bb']>(props.bb)
  45. expectType<ExpectedProps['cc']>(props.cc)
  46. expectType<ExpectedProps['dd']>(props.dd)
  47. expectType<ExpectedProps['ccc']>(props.ccc)
  48. expectType<ExpectedProps['ddd']>(props.ddd)
  49. // props should be readonly
  50. expectError((props.a = 1))
  51. // setup context
  52. return {
  53. c: ref(1),
  54. d: {
  55. e: ref('hi')
  56. }
  57. }
  58. },
  59. render() {
  60. const props = this.$props
  61. expectType<ExpectedProps['a']>(props.a)
  62. expectType<ExpectedProps['b']>(props.b)
  63. expectType<ExpectedProps['bb']>(props.bb)
  64. expectType<ExpectedProps['cc']>(props.cc)
  65. expectType<ExpectedProps['dd']>(props.dd)
  66. expectType<ExpectedProps['ccc']>(props.ccc)
  67. expectType<ExpectedProps['ddd']>(props.ddd)
  68. // props should be readonly
  69. expectError((props.a = 1))
  70. // should also expose declared props on `this`
  71. expectType<ExpectedProps['a']>(this.a)
  72. expectType<ExpectedProps['b']>(this.b)
  73. expectType<ExpectedProps['bb']>(this.bb)
  74. expectType<ExpectedProps['cc']>(this.cc)
  75. expectType<ExpectedProps['dd']>(this.dd)
  76. expectType<ExpectedProps['ccc']>(this.ccc)
  77. expectType<ExpectedProps['ddd']>(this.ddd)
  78. // props on `this` should be readonly
  79. expectError((this.a = 1))
  80. // assert setup context unwrapping
  81. expectType<number>(this.c)
  82. expectType<string>(this.d.e)
  83. // setup context properties should be mutable
  84. this.c = 2
  85. return null
  86. }
  87. })
  88. // Test TSX
  89. expectType<JSX.Element>(
  90. <MyComponent
  91. a={1}
  92. b="b"
  93. bb="bb"
  94. cc={['cc']}
  95. dd={['dd']}
  96. ccc={['ccc']}
  97. ddd={['ddd']}
  98. // should allow extraneous as attrs
  99. class="bar"
  100. // should allow key
  101. key={'foo'}
  102. // should allow ref
  103. ref={'foo'}
  104. />
  105. )
  106. // missing required props
  107. expectError(<MyComponent />)
  108. // wrong prop types
  109. expectError(
  110. <MyComponent a={'wrong type'} b="foo" dd={['foo']} ddd={['foo']} />
  111. )
  112. expectError(<MyComponent b="foo" dd={[123]} ddd={['foo']} />)
  113. })
  114. describe('type inference w/ optional props declaration', () => {
  115. const MyComponent = createComponent({
  116. setup(_props: { msg: string }) {
  117. return {
  118. a: 1
  119. }
  120. },
  121. render() {
  122. expectType<string>(this.$props.msg)
  123. // props should be readonly
  124. expectError((this.$props.msg = 'foo'))
  125. // should not expose on `this`
  126. expectError(this.msg)
  127. expectType<number>(this.a)
  128. return null
  129. }
  130. })
  131. expectType<JSX.Element>(<MyComponent msg="foo" />)
  132. expectError(<MyComponent />)
  133. expectError(<MyComponent msg={1} />)
  134. })
  135. describe('type inference w/ direct setup function', () => {
  136. const MyComponent = createComponent((_props: { msg: string }) => {})
  137. expectType<JSX.Element>(<MyComponent msg="foo" />)
  138. expectError(<MyComponent />)
  139. expectError(<MyComponent msg={1} />)
  140. })
  141. describe('type inference w/ array props declaration', () => {
  142. createComponent({
  143. props: ['a', 'b'],
  144. setup(props) {
  145. // props should be readonly
  146. expectError((props.a = 1))
  147. expectType<any>(props.a)
  148. expectType<any>(props.b)
  149. return {
  150. c: 1
  151. }
  152. },
  153. render() {
  154. expectType<any>(this.$props.a)
  155. expectType<any>(this.$props.b)
  156. expectError((this.$props.a = 1))
  157. expectType<any>(this.a)
  158. expectType<any>(this.b)
  159. expectType<number>(this.c)
  160. }
  161. })
  162. })
  163. describe('type inference w/ options API', () => {
  164. createComponent({
  165. props: { a: Number },
  166. setup() {
  167. return {
  168. b: 123
  169. }
  170. },
  171. data() {
  172. // Limitation: we cannot expose the return result of setup() on `this`
  173. // here in data() - somehow that would mess up the inference
  174. expectType<number | undefined>(this.a)
  175. return {
  176. c: this.a || 123
  177. }
  178. },
  179. computed: {
  180. d(): number {
  181. expectType<number>(this.b)
  182. return this.b + 1
  183. }
  184. },
  185. watch: {
  186. a() {
  187. expectType<number>(this.b)
  188. this.b + 1
  189. }
  190. },
  191. created() {
  192. // props
  193. expectType<number | undefined>(this.a)
  194. // returned from setup()
  195. expectType<number>(this.b)
  196. // returned from data()
  197. expectType<number>(this.c)
  198. // computed
  199. expectType<number>(this.d)
  200. },
  201. methods: {
  202. doSomething() {
  203. // props
  204. expectType<number | undefined>(this.a)
  205. // returned from setup()
  206. expectType<number>(this.b)
  207. // returned from data()
  208. expectType<number>(this.c)
  209. // computed
  210. expectType<number>(this.d)
  211. }
  212. },
  213. render() {
  214. // props
  215. expectType<number | undefined>(this.a)
  216. // returned from setup()
  217. expectType<number>(this.b)
  218. // returned from data()
  219. expectType<number>(this.c)
  220. // computed
  221. expectType<number>(this.d)
  222. }
  223. })
  224. })