mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Update on "[compiler] Special-case phi inference for mixed readonly type"
This allows us to handle common operations such as `useFragment(...).edges.nodes ?? []` where we have a `Phi(MixedReadonly, Array)`. The underlying pattern remains general-purpose and not Relay-specific, and any API that returns transitively "mixed" data (primitives, arrays, plain objects) can benefit from the same type refinement. [ghstack-poisoned]
This commit is contained in:
@@ -499,22 +499,12 @@ class Unifier {
|
||||
if (candidateType === null) {
|
||||
candidateType = resolved;
|
||||
} else if (!typeEquals(resolved, candidateType)) {
|
||||
function isArrayOrMixedReadonly(ty: Type): ty is ObjectType {
|
||||
return (
|
||||
ty.kind === 'Object' &&
|
||||
(ty.shapeId === BuiltInArrayId ||
|
||||
ty.shapeId === BuiltInMixedReadonlyId)
|
||||
);
|
||||
}
|
||||
if (
|
||||
isArrayOrMixedReadonly(resolved) &&
|
||||
isArrayOrMixedReadonly(candidateType)
|
||||
) {
|
||||
candidateType =
|
||||
resolved.shapeId === BuiltInArrayId ? resolved : candidateType;
|
||||
} else {
|
||||
const unionType = tryUnionTypes(resolved, candidateType);
|
||||
if (unionType === null) {
|
||||
candidateType = null;
|
||||
break;
|
||||
} else {
|
||||
candidateType = unionType;
|
||||
}
|
||||
} // else same type, continue
|
||||
}
|
||||
@@ -668,3 +658,39 @@ const RefLikeNameRE = /^(?:[a-zA-Z$_][a-zA-Z$_0-9]*)Ref$|^ref$/;
|
||||
function isRefLikeName(t: PropType): boolean {
|
||||
return RefLikeNameRE.test(t.objectName) && t.propertyName === 'current';
|
||||
}
|
||||
|
||||
function tryUnionTypes(ty1: Type, ty2: Type): Type | null {
|
||||
let readonlyType: Type;
|
||||
let otherType: Type;
|
||||
if (ty1.kind === 'Object' && ty1.shapeId === BuiltInMixedReadonlyId) {
|
||||
readonlyType = ty1;
|
||||
otherType = ty2;
|
||||
} else if (ty2.kind === 'Object' && ty2.shapeId === BuiltInMixedReadonlyId) {
|
||||
readonlyType = ty2;
|
||||
otherType = ty1;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (otherType.kind === 'Primitive') {
|
||||
/**
|
||||
* Union(Primitive | MixedReadonly) = MixedReadonly
|
||||
*
|
||||
* For example, `data ?? null` could return `data`, the fact that RHS
|
||||
* is a primitive doesn't guarantee the result is a primitive.
|
||||
*/
|
||||
return readonlyType;
|
||||
} else if (
|
||||
otherType.kind === 'Object' &&
|
||||
otherType.shapeId === BuiltInArrayId
|
||||
) {
|
||||
/**
|
||||
* Union(Array | MixedReadonly) = Array
|
||||
*
|
||||
* In practice this pattern means the result is always an array. Given
|
||||
* that this behavior requires opting-in to the mixedreadonly type
|
||||
* (via moduleTypeProvider) this seems like a reasonable heuristic.
|
||||
*/
|
||||
return otherType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user