-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
Copy pathhoistStatics.ts
139 lines (122 loc) · 3.62 KB
/
hoistStatics.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copied directly from:
// https://github.com/mridgway/hoist-non-react-statics/blob/main/src/index.js
// https://unpkg.com/browse/@types/hoist-non-react-statics@3.3.6/index.d.ts
/**
* Copyright 2015, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
import type { ForwardRefExoticComponent, MemoExoticComponent } from 'react'
import { ForwardRef, Memo, isMemo } from '../utils/react-is'
const REACT_STATICS = {
childContextTypes: true,
contextType: true,
contextTypes: true,
defaultProps: true,
displayName: true,
getDefaultProps: true,
getDerivedStateFromError: true,
getDerivedStateFromProps: true,
mixins: true,
propTypes: true,
type: true,
} as const
const KNOWN_STATICS = {
name: true,
length: true,
prototype: true,
caller: true,
callee: true,
arguments: true,
arity: true,
} as const
const FORWARD_REF_STATICS = {
$$typeof: true,
render: true,
defaultProps: true,
displayName: true,
propTypes: true,
} as const
const MEMO_STATICS = {
$$typeof: true,
compare: true,
defaultProps: true,
displayName: true,
propTypes: true,
type: true,
} as const
const TYPE_STATICS = {
[ForwardRef]: FORWARD_REF_STATICS,
[Memo]: MEMO_STATICS,
} as const
function getStatics(component: any) {
// React v16.11 and below
if (isMemo(component)) {
return MEMO_STATICS
}
// React v16.12 and above
return TYPE_STATICS[component['$$typeof']] || REACT_STATICS
}
export type NonReactStatics<
Source,
C extends {
[key: string]: true
} = {},
> = {
[key in Exclude<
keyof Source,
Source extends MemoExoticComponent<any>
? keyof typeof MEMO_STATICS | keyof C
: Source extends ForwardRefExoticComponent<any>
? keyof typeof FORWARD_REF_STATICS | keyof C
: keyof typeof REACT_STATICS | keyof typeof KNOWN_STATICS | keyof C
>]: Source[key]
}
const defineProperty = Object.defineProperty
const getOwnPropertyNames = Object.getOwnPropertyNames
const getOwnPropertySymbols = Object.getOwnPropertySymbols
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
const getPrototypeOf = Object.getPrototypeOf
const objectPrototype = Object.prototype
export default function hoistNonReactStatics<
Target,
Source,
CustomStatic extends {
[key: string]: true
} = {},
>(
targetComponent: Target,
sourceComponent: Source,
): Target & NonReactStatics<Source, CustomStatic> {
if (typeof sourceComponent !== 'string') {
// don't hoist over string (html) components
if (objectPrototype) {
const inheritedComponent = getPrototypeOf(sourceComponent)
if (inheritedComponent && inheritedComponent !== objectPrototype) {
hoistNonReactStatics(targetComponent, inheritedComponent)
}
}
let keys: (string | symbol)[] = getOwnPropertyNames(sourceComponent)
if (getOwnPropertySymbols) {
keys = keys.concat(getOwnPropertySymbols(sourceComponent))
}
const targetStatics = getStatics(targetComponent)
const sourceStatics = getStatics(sourceComponent)
for (let i = 0; i < keys.length; ++i) {
const key = keys[i]
if (
!KNOWN_STATICS[key as keyof typeof KNOWN_STATICS] &&
!(sourceStatics && sourceStatics[key as keyof typeof sourceStatics]) &&
!(targetStatics && targetStatics[key as keyof typeof targetStatics])
) {
const descriptor = getOwnPropertyDescriptor(sourceComponent, key)
try {
// Avoid failures from read-only properties
defineProperty(targetComponent, key, descriptor!)
} catch (e) {
// ignore
}
}
}
}
return targetComponent as any
}