createComponent.test-d.tsx 6.0 KB

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