Ver Fonte

fix(types): more precise types for class bindings (#8012)

Basil Gor há 2 anos atrás
pai
commit
46e33744c8

+ 36 - 0
packages/dts-test/tsx.test-d.tsx

@@ -17,6 +17,42 @@ expectType<JSX.Element>(
   <div style={[{ color: 'red' }, [{ fontSize: '1em' }]]} />
 )
 
+// allow undefined, string, object, array and nested array classes
+expectType<JSX.Element>(<div class={undefined} />)
+expectType<JSX.Element>(<div class={'foo'} />)
+expectType<JSX.Element>(<div class={['foo', undefined, 'bar']} />)
+expectType<JSX.Element>(<div class={[]} />)
+expectType<JSX.Element>(<div class={['foo', ['bar'], [['baz']]]} />)
+expectType<JSX.Element>(<div class={{ foo: true, bar: false, baz: true }} />)
+expectType<JSX.Element>(<div class={{}} />)
+expectType<JSX.Element>(
+  <div class={['foo', ['bar'], { baz: true }, [{ qux: true }]]} />
+)
+expectType<JSX.Element>(
+  <div
+    class={[
+      { foo: false },
+      { bar: 0 },
+      { baz: -0 },
+      { qux: '' },
+      { quux: null },
+      { corge: undefined },
+      { grault: NaN }
+    ]}
+  />
+)
+expectType<JSX.Element>(
+  <div
+    class={[
+      { foo: true },
+      { bar: 'not-empty' },
+      { baz: 1 },
+      { qux: {} },
+      { quux: [] }
+    ]}
+  />
+)
+
 // #7955
 expectType<JSX.Element>(<div style={[undefined, '', null, false]} />)
 

+ 7 - 1
packages/runtime-dom/src/jsx.ts

@@ -252,10 +252,16 @@ export type StyleValue =
   | CSSProperties
   | Array<StyleValue>
 
+export type ClassValue =
+  | undefined
+  | string
+  | Record<string | number, unknown>
+  | Array<ClassValue>
+
 export interface HTMLAttributes extends AriaAttributes, EventHandlers<Events> {
   innerHTML?: string
 
-  class?: any
+  class?: ClassValue
   style?: StyleValue
 
   // Standard HTML Attributes

+ 48 - 0
packages/shared/__tests__/normalizeProp.spec.ts

@@ -1,6 +1,10 @@
 import { normalizeClass, parseStringStyle } from '../src'
 
 describe('normalizeClass', () => {
+  test('handles undefined correctly', () => {
+    expect(normalizeClass(undefined)).toEqual('')
+  })
+
   test('handles string correctly', () => {
     expect(normalizeClass('foo')).toEqual('foo')
   })
@@ -11,12 +15,56 @@ describe('normalizeClass', () => {
     )
   })
 
+  test('handles empty array correctly', () => {
+    expect(normalizeClass([])).toEqual('')
+  })
+
+  test('handles nested array correctly', () => {
+    expect(normalizeClass(['foo', ['bar'], [['baz']]])).toEqual('foo bar baz')
+  })
+
   test('handles object correctly', () => {
     expect(normalizeClass({ foo: true, bar: false, baz: true })).toEqual(
       'foo baz'
     )
   })
 
+  test('handles empty object correctly', () => {
+    expect(normalizeClass({})).toEqual('')
+  })
+
+  test('handles arrays and objects correctly', () => {
+    expect(
+      normalizeClass(['foo', ['bar'], { baz: true }, [{ qux: true }]])
+    ).toEqual('foo bar baz qux')
+  })
+
+  test('handles array of objects with falsy values', () => {
+    expect(
+      normalizeClass([
+        { foo: false },
+        { bar: 0 },
+        { baz: -0 },
+        { qux: '' },
+        { quux: null },
+        { corge: undefined },
+        { grault: NaN }
+      ])
+    ).toEqual('')
+  })
+
+  test('handles array of objects with truthy values', () => {
+    expect(
+      normalizeClass([
+        { foo: true },
+        { bar: 'not-empty' },
+        { baz: 1 },
+        { qux: {} },
+        { quux: [] }
+      ])
+    ).toEqual('foo bar baz qux quux')
+  })
+
   // #6777
   test('parse multi-line inline style', () => {
     expect(