@@ -174,12 +174,10 @@ export type ExtractDefaultPropTypes<O> = O extends object
174
174
{ [ K in keyof Pick < O , DefaultKeys < O > > ] : InferPropType < O [ K ] > }
175
175
: { }
176
176
177
- type NormalizedProp =
178
- | null
179
- | ( PropOptions & {
180
- [ BooleanFlags . shouldCast ] ?: boolean
181
- [ BooleanFlags . shouldCastTrue ] ?: boolean
182
- } )
177
+ type NormalizedProp = PropOptions & {
178
+ [ BooleanFlags . shouldCast ] ?: boolean
179
+ [ BooleanFlags . shouldCastTrue ] ?: boolean
180
+ }
183
181
184
182
// normalized value is a tuple of the actual normalized options
185
183
// and an array of prop keys that need value casting (booleans and defaults)
@@ -564,16 +562,32 @@ export function normalizePropsOptions(
564
562
const opt = raw [ key ]
565
563
const prop : NormalizedProp = ( normalized [ normalizedKey ] =
566
564
isArray ( opt ) || isFunction ( opt ) ? { type : opt } : extend ( { } , opt ) )
567
- if ( prop ) {
568
- const booleanIndex = getTypeIndex ( Boolean , prop . type )
569
- const stringIndex = getTypeIndex ( String , prop . type )
570
- prop [ BooleanFlags . shouldCast ] = booleanIndex > - 1
571
- prop [ BooleanFlags . shouldCastTrue ] =
572
- stringIndex < 0 || booleanIndex < stringIndex
573
- // if the prop needs boolean casting or default value
574
- if ( booleanIndex > - 1 || hasOwn ( prop , 'default' ) ) {
575
- needCastKeys . push ( normalizedKey )
565
+ const propType = prop . type
566
+ let shouldCastTrue = true
567
+
568
+ const checkForBoolean = (
569
+ type : PropConstructor | true | null | undefined ,
570
+ ) => {
571
+ const typeName = isFunction ( type ) && type . name
572
+
573
+ // If we find `String` before `Boolean`, e.g. `[String, Boolean]`, we need to handle the casting slightly
574
+ // differently. Props passed as `<Comp checked="">` or `<Comp checked="checked">` will either be treated as
575
+ // strings or converted to a boolean `true`, depending on the order of the types.
576
+ if ( typeName === 'String' ) {
577
+ shouldCastTrue = false
576
578
}
579
+
580
+ return typeName === 'Boolean'
581
+ }
582
+
583
+ const shouldCast = isArray ( propType )
584
+ ? propType . some ( checkForBoolean )
585
+ : checkForBoolean ( propType )
586
+ prop [ BooleanFlags . shouldCast ] = shouldCast
587
+ prop [ BooleanFlags . shouldCastTrue ] = shouldCastTrue
588
+ // if the prop needs boolean casting or default value
589
+ if ( shouldCast || hasOwn ( prop , 'default' ) ) {
590
+ needCastKeys . push ( normalizedKey )
577
591
}
578
592
}
579
593
}
@@ -595,6 +609,7 @@ function validatePropName(key: string) {
595
609
return false
596
610
}
597
611
612
+ // dev only
598
613
// use function string name to check type constructors
599
614
// so that it works across vms / iframes.
600
615
function getType ( ctor : Prop < any > ) : string {
@@ -617,22 +632,6 @@ function getType(ctor: Prop<any>): string {
617
632
return ''
618
633
}
619
634
620
- function isSameType ( a : Prop < any > , b : Prop < any > ) : boolean {
621
- return getType ( a ) === getType ( b )
622
- }
623
-
624
- function getTypeIndex (
625
- type : Prop < any > ,
626
- expectedTypes : PropType < any > | void | null | true ,
627
- ) : number {
628
- if ( isArray ( expectedTypes ) ) {
629
- return expectedTypes . findIndex ( t => isSameType ( t , type ) )
630
- } else if ( isFunction ( expectedTypes ) ) {
631
- return isSameType ( expectedTypes , type ) ? 0 : - 1
632
- }
633
- return - 1
634
- }
635
-
636
635
/**
637
636
* dev only
638
637
*/
0 commit comments