Ajout des types null et undefined

Ajout des paramètres strictNullChecks et strictPropertyInitialization
dans la configuration TypeScript et correction des environ 70 erreurs
de typage résultantes.
pull/817/head
Maxime Quandalle 2019-12-18 17:47:43 +01:00
parent d89833502b
commit ca5b7cc2df
No known key found for this signature in database
GPG Key ID: 428641C03D29CA10
37 changed files with 216 additions and 181 deletions

View File

@ -95,9 +95,10 @@ export default class Provider extends PureComponent<ProviderProps> {
this.props.tracker.disconnectFromHistory()
}
render() {
const iframeCouleur = new URLSearchParams(
document?.location.search.substring(1)
).get('couleur')
const iframeCouleur =
new URLSearchParams(document?.location.search.substring(1)).get(
'couleur'
) ?? undefined
return (
// If IE < 11 display nothing
<ReduxProvider store={this.store}>

View File

@ -12,8 +12,8 @@ type PushType = (args: PushArgs) => void
export default class Tracker {
push: PushType
unlistenFromHistory: () => void
previousPath: string
unlistenFromHistory: (() => void) | undefined
previousPath: string | undefined
constructor(pushFunction: PushType = args => window._paq.push(args)) {
if (typeof window !== 'undefined') window._paq = window._paq || []

View File

@ -43,12 +43,17 @@ type DeletePreviousSimulationAction = {
type: 'DELETE_PREVIOUS_SIMULATION'
}
type SetExempleAction = {
type: 'SET_EXAMPLE'
name: null | string
situation?: object
dottedName?: string
}
type SetExempleAction =
| {
type: 'SET_EXAMPLE'
name: null
}
| {
type: 'SET_EXAMPLE'
name: string
situation: object
dottedName: DottedName
}
type ResetSimulationAction = ReturnType<typeof resetSimulation>
type UpdateAction = ReturnType<typeof updateSituation>
@ -142,7 +147,8 @@ export const goBackToSimulation = (): ThunkResult<void> => (
{ history }
) => {
dispatch({ type: 'SET_EXAMPLE', name: null })
history.push(getState().simulation.url)
const url = getState().simulation?.url
url && history.push(url)
}
export function loadPreviousSimulation() {
@ -155,7 +161,7 @@ export function hideControl(id: string) {
return { type: 'HIDE_CONTROL', id } as const
}
export const explainVariable = (variableName = null) =>
export const explainVariable = (variableName: DottedName | null = null) =>
({
type: 'EXPLAIN_VARIABLE',
variableName

View File

@ -30,7 +30,9 @@ export type Etablissement = {
denomination?: string
}
async function searchFullText(text: string): Promise<Array<Etablissement>> {
async function searchFullText(
text: string
): Promise<Array<Etablissement> | null> {
const response = await fetch(
`https://entreprise.data.gouv.fr/api/sirene/v1/full_text/${text}?per_page=5`
)

View File

@ -25,11 +25,11 @@ export default function CurrencyInput({
const [initialValue, setInitialValue] = useState(valueProp)
const [currentValue, setCurrentValue] = useState(valueProp)
const onChangeDebounced = useRef(
debounceTimeout ? debounce(debounceTimeout, onChange) : onChange
debounceTimeout && onChange ? debounce(debounceTimeout, onChange) : onChange
)
// We need some mutable reference because the <NumberFormat /> component doesn't provide
// the DOM `event` in its custom `onValueChange` handler
const nextValue = useRef(null)
const nextValue = useRef('')
const inputRef = useRef<HTMLInputElement>()
@ -51,8 +51,8 @@ export default function CurrencyInput({
...event.target,
value: nextValue.current
}
nextValue.current = null
onChangeDebounced.current(event)
nextValue.current = ''
onChangeDebounced.current?.(event)
}
const {
@ -71,7 +71,7 @@ export default function CurrencyInput({
<div
className={classnames(className, 'currencyInput__container')}
{...(valueLength > 5 ? { style: { width } } : {})}
onClick={() => inputRef.current.focus()}
onClick={() => inputRef.current?.focus()}
>
{!currentValue && isCurrencyPrefixed && currencySymbol}
<NumberFormat

View File

@ -6,7 +6,7 @@ import { Trans } from 'react-i18next'
type Props = { onEnd: () => void; onCancel: () => void }
export default function FeedbackForm({ onEnd, onCancel }: Props) {
const formRef = useRef<HTMLFormElement>()
const formRef = useRef<HTMLFormElement>(null)
const tracker = useContext(TrackerContext)
const handleFormSubmit = (e: React.FormEvent): void => {
@ -14,7 +14,7 @@ export default function FeedbackForm({ onEnd, onCancel }: Props) {
e.preventDefault()
fetch('/', {
method: 'POST',
body: new FormData(formRef.current)
body: new FormData(formRef.current ?? undefined)
})
onEnd()
}

View File

@ -7,7 +7,9 @@ import { Etablissement, searchDenominationOrSiren } from '../api/sirene'
import { debounce } from '../utils'
export default function Search() {
const [searchResults, setSearchResults] = useState<Array<Etablissement>>()
const [searchResults, setSearchResults] = useState<Array<
Etablissement
> | null>()
const [isLoading, setLoadingState] = useState(false)
const handleSearch = useCallback(

View File

@ -30,24 +30,26 @@ export default function QuickLinks() {
toPairs
)(quickLinks)
if (links.length < 1) {
return null
}
return (
!!links.length && (
<span>
<small>Questions :</small>
{links.map(([label, dottedName]) => (
<button
key={dottedName}
className={`ui__ link-button ${
dottedName === currentQuestion ? 'active' : ''
}`}
css="margin: 0 0.4rem !important"
onClick={() => dispatch(goToQuestion(dottedName))}
>
<T k={'quicklinks.' + label}>{label}</T>
</button>
))}{' '}
{/* <button className="ui__ link-button">Voir la liste</button> */}
</span>
)
<span>
<small>Questions :</small>
{links.map(([label, dottedName]) => (
<button
key={dottedName}
className={`ui__ link-button ${
dottedName === currentQuestion ? 'active' : ''
}`}
css="margin: 0 0.4rem !important"
onClick={() => dispatch(goToQuestion(dottedName))}
>
<T k={'quicklinks.' + label}>{label}</T>
</button>
))}{' '}
{/* <button className="ui__ link-button">Voir la liste</button> */}
</span>
)
}

View File

@ -34,7 +34,7 @@ export default function SalaryExplanation() {
const showDistributionFirst = useSelector(
(state: RootState) => !state.conversationSteps.foldedSteps.length
)
const distributionRef = useRef<HTMLDivElement>()
const distributionRef = useRef<HTMLDivElement>(null)
return (
<ErrorBoundary>
<Animate.fromTop key={showDistributionFirst.toString()}>
@ -51,7 +51,7 @@ export default function SalaryExplanation() {
<button
className="ui__ small simple button"
onClick={() =>
distributionRef.current.scrollIntoView({
distributionRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
@ -130,7 +130,7 @@ function PaySlipSection() {
return (
<section>
<h2>
{unit.endsWith('mois') ? (
{unit?.endsWith('mois') ? (
<Trans>Fiche de paie</Trans>
) : (
<Trans>Détail annuel des cotisations</Trans>

View File

@ -17,6 +17,8 @@ type SearchBarProps = {
finally?: () => void
}
type Option = Pick<Rule, 'dottedName' | 'name' | 'title'>
export default function SearchBar({
rules,
showDefaultList,
@ -24,7 +26,7 @@ export default function SearchBar({
}: SearchBarProps) {
const sitePaths = useContext(SitePathsContext)
const [input, setInput] = useState('')
const [selectedOption, setSelectedOption] = useState(null)
const [selectedOption, setSelectedOption] = useState<Option | null>(null)
const [results, setResults] = useState([])
const { i18n } = useTranslation()
@ -44,7 +46,7 @@ export default function SearchBar({
return <ul>{options.map(option => renderOption(option))}</ul>
}
let renderOption = option => {
let renderOption = (option: Option) => {
let { title, dottedName, name } = option
return (
<li
@ -86,14 +88,14 @@ export default function SearchBar({
>
<Highlighter
searchWords={[input]}
textToHighlight={title || capitalise0(name)}
textToHighlight={title || capitalise0(name) || ''}
/>
</Link>
</li>
)
}
if (selectedOption != null) {
if (selectedOption !== null) {
finallyCallback && finallyCallback()
return (
<Redirect

View File

@ -87,7 +87,7 @@ export default function StackedBarChart({ data }: StackedBarChartProps) {
const [intersectionRef, displayChart] = useDisplayOnIntersecting({
threshold: 0.5
})
const percentages = roundedPercentages(data.map(d => d.nodeValue))
const percentages = roundedPercentages(data.map(d => d.nodeValue || 0))
const dataWithPercentage = data.map((data, index) => ({
...data,
percentage: percentages[index]

View File

@ -274,7 +274,7 @@ let TargetInputOrValue = ({
</>
) : (
<span>
{Number.isNaN(value) ? '—' : formatCurrency(value, language)}
{value && Number.isNaN(value) ? '—' : formatCurrency(value, language)}
</span>
)}
{target.dottedName.includes('prix du travail') && <AidesGlimpse />}

View File

@ -47,7 +47,7 @@ export default function Value({
if (
(nilValueSymbol !== undefined && nodeValue === 0) ||
Number.isNaN(nodeValue) ||
(nodeValue && Number.isNaN(nodeValue)) ||
nodeValue === null
)
return (
@ -63,7 +63,7 @@ export default function Value({
(nodeValue as any).nom
) : valueType === 'boolean' ? (
booleanTranslations[language][nodeValue]
) : (
) : nodeValue !== undefined ? (
formatValue({
minimumFractionDigits,
maximumFractionDigits,
@ -71,7 +71,7 @@ export default function Value({
unit,
value: nodeValue
})
)
) : null
return nodeValue == undefined ? null : (
<span css={style(customCSS)} className="value">
{negative ? '-' : ''}

View File

@ -18,9 +18,9 @@ export default function InputSuggestions({
onFirstClick,
unit
}: InputSuggestionsProps) {
const [suggestion, setSuggestion] = useState(null)
const [suggestion, setSuggestion] = useState<number>()
const { t } = useTranslation()
const defaultUnit = parseUnit(useSelector(defaultUnitsSelector)[0])
const defaultUnit = parseUnit(useSelector(defaultUnitsSelector)[0] ?? '')
if (!suggestions) return null
return (

View File

@ -11,7 +11,7 @@ type AnimatedTargetValueProps = {
children?: React.ReactNode
}
const formatDifference: typeof formatCurrency = (difference, language) => {
const formatDifference = (difference: number, language: string) => {
const prefix = difference > 0 ? '+' : ''
return prefix + formatCurrency(difference, language)
}
@ -25,12 +25,12 @@ export default function AnimatedTargetValue({
// We don't want to show the animated if the difference comes from a change in the unit
const currentUnit = useSelector(
(state: RootState) => state.simulation.defaultUnits[0]
(state: RootState) => state?.simulation?.defaultUnits[0]
)
const previousUnit = useRef(currentUnit)
const difference =
previousValue.current === value || Number.isNaN(value)
previousValue.current === value || (value && Number.isNaN(value))
? null
: (value || 0) - (previousValue.current || 0)
const shouldDisplayDifference =
@ -44,7 +44,7 @@ export default function AnimatedTargetValue({
return (
<>
<span className="Rule-value">
{shouldDisplayDifference && (
{shouldDisplayDifference && difference !== null && (
<Evaporate
style={{
color: difference > 0 ? 'chartreuse' : 'red',

View File

@ -15,7 +15,7 @@ export const getInitialState = (key: string) => {
return
}
try {
return JSON.parse(safeLocalStorage.getItem(key))
return JSON.parse(value)
} catch (e) {
console.warn(e)
return null

View File

@ -5,7 +5,7 @@ export default function({
rootMargin,
threshold = 0
}: IntersectionObserverInit): [React.RefObject<HTMLDivElement>, boolean] {
const ref = useRef<HTMLDivElement>()
const ref = useRef<HTMLDivElement>(null)
const [wasOnScreen, setWasOnScreen] = useState(false)
useEffect(() => {
@ -13,7 +13,7 @@ export default function({
([entry]) => {
if (entry.isIntersecting) {
setWasOnScreen(entry.isIntersecting)
observer.unobserve(ref.current)
ref.current && observer.unobserve(ref.current)
}
},
{
@ -23,11 +23,11 @@ export default function({
}
)
const node = ref.current
if (ref.current) {
if (node) {
observer.observe(node)
}
return () => {
observer.unobserve(node)
node && observer.unobserve(node)
}
}, [root, rootMargin, threshold])

View File

@ -97,7 +97,7 @@ export type ThemeColours = ReturnType<typeof generateTheme>
export const ThemeColoursContext = createContext<ThemeColours>(generateTheme())
type ProviderProps = {
colour: string
colour?: string
children: React.ReactNode
}

View File

@ -35,7 +35,7 @@ export let numberFormatter = ({
}).format(value)
}
export const currencyFormat = (language: string) => ({
export const currencyFormat = (language: string | undefined) => ({
isCurrencyPrefixed: !!numberFormatter({ language, style: 'currency' })(
12
).match(/^€/),
@ -43,13 +43,13 @@ export const currencyFormat = (language: string) => ({
decimalSeparator: numberFormatter({ language })(0.1).charAt(1)
})
export const formatCurrency = (value: number, language: string) => {
export const formatCurrency = (value: number | undefined, language: string) => {
return value == null
? ''
: formatValue({ unit: '€', language, value }).replace(/^(-)?€/, '$1€\u00A0')
}
export const formatPercentage = value =>
export const formatPercentage = (value: number | undefined) =>
value == null
? ''
: formatValue({ unit: '%', value, maximumFractionDigits: 2 })
@ -58,7 +58,7 @@ export type formatValueOptions = {
maximumFractionDigits?: number
minimumFractionDigits?: number
language?: string
unit: Unit | string
unit?: Unit | string
value: number
}
@ -72,7 +72,7 @@ export function formatValue({
if (typeof value !== 'number') {
return value
}
const serializedUnit = serialiseUnit(unit, value, language)
const serializedUnit = unit ? serialiseUnit(unit, value, language) : undefined
switch (serializedUnit) {
case '€':

View File

@ -15,6 +15,7 @@ import {
toPairs,
values
} from 'ramda'
import { DottedName } from 'Types/rule'
/*
COLLECTE DES VARIABLES MANQUANTES
@ -31,7 +32,12 @@ import {
missingVariables: {variable: [objectives]}
*/
export let collectMissingVariablesByTarget = (targets = []) =>
type Explanation = {
missingVariables: Array<DottedName>
dottedName: DottedName
}
export let collectMissingVariablesByTarget = (targets: Explanation[] = []) =>
fromPairs(targets.map(target => [target.dottedName, target.missingVariables]))
export let getNextSteps = missingVariablesByTarget => {

View File

@ -24,8 +24,9 @@ function MecanismEncadrement({ nodeValue, explanation, unit }) {
{!explanation.plancher.isDefault && (
<span
css={
nodeValue === val(explanation.plancher) &&
'background: yellow'
nodeValue === val(explanation.plancher)
? 'background: yellow'
: {}
}
>
<strong className="key">Minimum : </strong>
@ -35,7 +36,9 @@ function MecanismEncadrement({ nodeValue, explanation, unit }) {
{!explanation.plafond.isDefault && (
<span
css={
nodeValue === val(explanation.plafond) && 'background: yellow'
nodeValue === val(explanation.plafond)
? 'background: yellow'
: {}
}
>
<strong className="key">Plafonné à : </strong>

View File

@ -131,14 +131,16 @@ type CacheMeta = {
}
}
export let analyseMany = (parsedRules, targetNames, defaultUnits = []) => (
situationGate: (name: DottedName) => any
) => {
export let analyseMany = (
parsedRules,
targetNames,
defaultUnits: Array<string> = []
) => (situationGate: (name: DottedName) => any) => {
// TODO: we should really make use of namespaces at this level, in particular
// setRule in Rule.js needs to get smarter and pass dottedName
defaultUnits = defaultUnits.map(parseUnit)
const defaultParsedUnits = defaultUnits.map(parseUnit)
let cache = {
_meta: { contextRule: [], defaultUnits } as CacheMeta
_meta: { contextRule: [], defaultUnits: defaultParsedUnits } as CacheMeta
}
let parsedTargets = targetNames.map(t => {

View File

@ -86,7 +86,7 @@ let noUnit = { numerators: [], denominators: [] }
export let inferUnit = (
operator: SupportedOperators,
rawUnits: Array<Unit>
): Unit => {
): Unit | undefined => {
let units = rawUnits.map(u => u || noUnit)
if (operator === '*')
return simplify({
@ -109,7 +109,7 @@ export let inferUnit = (
return rawUnits.find(u => u)
}
return null
return undefined
}
export let removeOnce = <T>(

View File

@ -78,10 +78,7 @@ function companyCreationChecklist(
}
}
function companyStatusChoice(
state: LegalStatus = null,
action: Action
): LegalStatus {
function companyStatusChoice(state: LegalStatus | null = null, action: Action) {
if (action.type === 'RESET_COMPANY_STATUS_CHOICE') {
return null
}
@ -131,7 +128,7 @@ export type Company = {
siren: string
catégorieJuridique?: string
statutJuridique?: string
dateDébutActivité?: Date
dateDébutActivité?: string
isAutoEntrepreneur?: boolean
isDirigeantMajoritaire?: boolean
localisation?: GeoDetails & {
@ -140,7 +137,7 @@ export type Company = {
}
}
function existingCompany(state: Company = null, action): Company {
function existingCompany(state: Company | null = null, action): Company | null {
if (!action.type.startsWith('EXISTING_COMPANY::')) {
return state
}

View File

@ -1,6 +1,6 @@
import { Action } from 'Actions/actions'
import { Analysis } from 'Engine/traverse'
import { areUnitConvertible, convertUnit, parseUnit } from 'Engine/units'
import { areUnitConvertible, convertUnit, parseUnit, Unit } from 'Engine/units'
import {
defaultTo,
dissoc,
@ -22,10 +22,7 @@ import i18n, { AvailableLangs } from '../i18n'
import inFranceAppReducer from './inFranceAppReducer'
import storageRootReducer from './storageReducer'
function explainedVariable(
state: DottedName = null,
action: Action
): DottedName {
function explainedVariable(state: DottedName | null = null, action: Action) {
switch (action.type) {
case 'EXPLAIN_VARIABLE':
return action.variableName
@ -36,17 +33,23 @@ function explainedVariable(
}
}
function currentExample(state = null, action: Action) {
type Example = null | {
name: string
situation: object
dottedName: DottedName
defaultUnits?: Array<Unit>
}
function currentExample(state: Example = null, action: Action): Example {
switch (action.type) {
case 'SET_EXAMPLE':
const { situation, name, dottedName } = action
return name != null ? { name, situation, dottedName } : null
return action.name != null ? action : null
default:
return state
}
}
function situationBranch(state: number = null, action: Action): number {
function situationBranch(state: number | null = null, action: Action) {
switch (action.type) {
case 'SET_SITUATION_BRANCH':
return action.id
@ -55,10 +58,7 @@ function situationBranch(state: number = null, action: Action): number {
}
}
function activeTargetInput(
state: DottedName | null = null,
action: Action
): DottedName | null {
function activeTargetInput(state: DottedName | null = null, action: Action) {
switch (action.type) {
case 'SET_ACTIVE_TARGET_INPUT':
return action.name
@ -83,7 +83,7 @@ function lang(
type ConversationSteps = {
foldedSteps: Array<DottedName>
unfoldedStep?: DottedName
unfoldedStep?: DottedName | null
}
function conversationSteps(
@ -178,9 +178,9 @@ export type Simulation = {
}
function simulation(
state: Simulation = null,
state: Simulation | null = null,
action: Action,
analysis: Analysis | Array<Analysis>
analysis: Analysis | Array<Analysis> | null
): Simulation | null {
if (action.type === 'SET_SIMULATION') {
const { config, url } = action
@ -272,9 +272,9 @@ const mainReducer = (state, action: Action) =>
rules: defaultTo(null) as Reducer<Array<Rule>>,
explainedVariable,
// We need to access the `rules` in the simulation reducer
simulation: (a: Simulation | null, b: Action): Simulation =>
simulation: (a: Simulation | null = null, b: Action): Simulation | null =>
simulation(a, b, a && analysisWithDefaultsSelector(state)),
previousSimulation: defaultTo(null) as Reducer<SavedSimulation>,
previousSimulation: defaultTo(null) as Reducer<SavedSimulation | null>,
currentExample,
situationBranch,
activeTargetInput,
@ -285,6 +285,6 @@ export default reduceReducers<RootState>(
existingCompanyRootReducer,
mainReducer as any,
storageRootReducer as any
)
) as Reducer<RootState>
export type RootState = ReturnType<typeof mainReducer>

View File

@ -27,7 +27,7 @@ import {
zipWith
} from 'ramda'
import { useSelector } from 'react-redux'
import { RootState } from 'Reducers/rootReducer'
import { RootState, Simulation } from 'Reducers/rootReducer'
import { createSelector, createSelectorCreator, defaultMemoize } from 'reselect'
import { DottedName } from 'Types/rule'
import { mapOrApply } from '../utils'
@ -51,7 +51,7 @@ export let ruleDefaultsSelector = createSelector([flatRulesSelector], rules =>
export let targetNamesSelector = (state: RootState) => {
let objectifs = configSelector(state).objectifs
if (!objectifs || !Array.isArray(objectifs)) {
return null
return []
}
const targetNames = [].concat(
...(objectifs as any).map(objectifOrGroup =>
@ -121,9 +121,7 @@ const createSituationBrancheSelector = (
situation,
branches,
configSituation
):
| RootState['simulation']['situation']
| Array<RootState['simulation']['situation']> => {
): Simulation['situation'] | Array<Simulation['situation']> => {
if (branches) {
return branches.map(({ situation: branchSituation }) => ({
...configSituation,
@ -215,7 +213,7 @@ export let exampleAnalysisSelector = createSelector(
rules,
dottedName,
(dottedName: DottedName) => situation[dottedName],
example.defaultUnits
example?.defaultUnits
)
)

View File

@ -12,6 +12,7 @@ import {
mergeWith,
sortBy
} from 'ramda'
import { RootState } from 'Reducers/rootReducer'
import { LegalStatusRequirements, State } from 'Types/companyTypes'
const LEGAL_STATUS_DETAILS = {
@ -125,9 +126,7 @@ export const possibleStatusSelector = (state: {
}): Record<LegalStatus, boolean> =>
possibleStatus(state.inFranceApp.companyLegalStatus)
export const nextQuestionSelector = (state: {
inFranceApp: State
}): Question => {
export const nextQuestionSelector = (state: RootState): Question | null => {
const legalStatusRequirements = state.inFranceApp.companyLegalStatus
const questionAnswered = Object.keys(legalStatusRequirements) as Array<
Question
@ -167,7 +166,7 @@ export const nextQuestionSelector = (state: {
}
export const nextQuestionUrlSelector = (
state: { inFranceApp: State },
state: RootState,
{ sitePaths }: { sitePaths: SitePaths }
) => {
const nextQuestion = nextQuestionSelector(state)

View File

@ -98,7 +98,7 @@ export let analysisToCotisations = (analysis: Analysis) => {
]
.map(name => analysis.cache[name])
.map(pathOr([], ['explanation', 'formule', 'explanation', 'explanation']))
.reduce(concat, [])
.reduce(concat as any, [])
const cotisations = pipe(
map((rule: any) =>

View File

@ -1,10 +1,10 @@
import { RootState } from 'Reducers/rootReducer'
import { RootState, Simulation } from 'Reducers/rootReducer'
// Note: it is currently not possible to define SavedSimulation as the return
// type of the currentSimulationSelector function because the type would then
// circulary reference itself.
export type SavedSimulation = {
situation: RootState['simulation']['situation']
situation: Simulation['situation']
activeTargetInput: RootState['activeTargetInput']
foldedSteps: RootState['conversationSteps']['foldedSteps']
}
@ -13,21 +13,25 @@ export const currentSimulationSelector = (
state: RootState
): SavedSimulation => {
return {
situation: state.simulation.situation,
situation: state.simulation?.situation ?? {},
activeTargetInput: state.activeTargetInput,
foldedSteps: state.conversationSteps.foldedSteps
}
}
export const createStateFromSavedSimulation = (state: RootState) =>
state.previousSimulation && {
activeTargetInput: state.previousSimulation.activeTargetInput,
simulation: {
...state.simulation,
situation: state.previousSimulation.situation || {}
},
conversationSteps: {
foldedSteps: state.previousSimulation.foldedSteps
},
previousSimulation: null
}
export const createStateFromSavedSimulation = (
state: RootState
): Partial<RootState> =>
state.previousSimulation
? {
activeTargetInput: state.previousSimulation.activeTargetInput,
simulation: {
...state.simulation,
situation: state.previousSimulation.situation || {}
} as Simulation,
conversationSteps: {
foldedSteps: state.previousSimulation.foldedSteps
},
previousSimulation: null
}
: {}

View File

@ -15,7 +15,7 @@ export default function AfterRegistration() {
(state: RootState) => state.inFranceApp.companyStatusChoice
)
const { t } = useTranslation()
const isAutoentrepreneur = statutChoisi.match('auto-entrepreneur')
const isAutoentrepreneur = statutChoisi?.match('auto-entrepreneur')
return (
<Animate.fromBottom>

View File

@ -38,20 +38,21 @@ export default function PreviousAnswers() {
const legalStatus = useSelector(
(state: RootState) => state.inFranceApp.companyLegalStatus
)
if (Object.values(legalStatus).length < 1) {
return null
}
return (
!!Object.values(legalStatus).length && (
<ul css="margin-bottom: -1rem;">
{Object.entries(legalStatus).map(
([key, value]) =>
!isNil(value) && (
<li key={key}>
<Link to={sitePaths.créer.guideStatut[key]}>
{requirementToText(key as any, value as any)}
</Link>
</li>
)
)}
</ul>
)
<ul css="margin-bottom: -1rem;">
{Object.entries(legalStatus).map(
([key, value]) =>
!isNil(value) && (
<li key={key}>
<Link to={sitePaths.créer.guideStatut[key]}>
{requirementToText(key as any, value as any)}
</Link>
</li>
)
)}
</ul>
)
}

View File

@ -21,8 +21,10 @@ export default function IntegrationTest() {
script.src = window.location.origin + '/simulateur-iframe-integration.js'
script.dataset.module = currentModule
script.dataset.couleur = colour
domNode.current.innerHTML = ''
domNode.current.appendChild(script)
if (domNode.current) {
domNode.current.innerHTML = ''
domNode.current.appendChild(script)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [version])
return (

View File

@ -11,6 +11,7 @@ const aideMidiPyrenéesAutoEntrepreneur = (state: RootState) => {
return false
}
return (
company.dateDébutActivité &&
new Date(company.dateDébutActivité) > new Date('2019-04-01') &&
company.isAutoEntrepreneur &&
company.localisation &&

View File

@ -20,7 +20,7 @@ import * as Animate from 'Ui/animate'
import AideOrganismeLocal from './AideOrganismeLocal'
import businessPlan from './businessPlan.svg'
const infereRégimeFromCompanyDetails = (company: Company) => {
const infereRégimeFromCompanyDetails = (company: Company | null) => {
if (!company) {
return null
}
@ -28,14 +28,14 @@ const infereRégimeFromCompanyDetails = (company: Company) => {
return 'auto-entrepreneur'
}
if (
['EI', 'EURL'].includes(company.statutJuridique) ||
['EI', 'EURL'].includes(company.statutJuridique ?? '') ||
(company.statutJuridique === 'SARL' && company.isDirigeantMajoritaire)
) {
return 'indépendant'
}
if (
['SASU', 'SAS'].includes(company.statutJuridique) ||
['SASU', 'SAS'].includes(company.statutJuridique ?? '') ||
(company.statutJuridique === 'SARL' && !company.isDirigeantMajoritaire)
) {
return 'assimilé-salarié'
@ -193,7 +193,7 @@ export default function SocialSecurity() {
}
type CompanySectionProps = {
company: Company
company: Company | null
}
const CompanySection = ({ company }: CompanySectionProps) => {
@ -203,7 +203,7 @@ const CompanySection = ({ company }: CompanySectionProps) => {
false
)
const companyRef = useRef(null)
const companyRef = useRef<Company | null>(null)
useEffect(() => {
if (companyRef.current !== company) {
companyRef.current = company

View File

@ -147,12 +147,13 @@ function WarningRegimeSpecial() {
const situation = useSelector(situationSelector)
const recettes = situation['artiste-auteur . revenus . BNC . recettes']
const showWarning = recettes !== 0 && recettes >= 70000
if (!showWarning) {
return null
}
return (
showWarning && (
<li>
Vos revenus ne vous permettent pas d'opter pour le régime micro-BNC.
</li>
)
<li>
Vos revenus ne vous permettent pas d'opter pour le régime micro-BNC.
</li>
)
}
@ -179,23 +180,25 @@ function CotisationsResult() {
setDisplay(true)
}
if (!display) {
return null
}
return (
display && (
<Animate.appear>
<ResultBlock className="ui__ card">
<ResultLabel>Montant des cotisations</ResultLabel>
<RuleLink dottedName={cotisationRule.dottedName}>
{formatValue({
value: cotisationRule.nodeValue,
language: 'fr',
unit: '€',
maximumFractionDigits: 0
})}
</RuleLink>
</ResultBlock>
{cotisationRule.nodeValue ? <RepartitionCotisations /> : null}
</Animate.appear>
)
<Animate.appear>
<ResultBlock className="ui__ card">
<ResultLabel>Montant des cotisations</ResultLabel>
<RuleLink dottedName={cotisationRule.dottedName}>
{formatValue({
value: cotisationRule.nodeValue,
language: 'fr',
unit: '€',
maximumFractionDigits: 0
})}
</RuleLink>
</ResultBlock>
{cotisationRule.nodeValue ? <RepartitionCotisations /> : null}
</Animate.appear>
)
}

View File

@ -1,14 +1,14 @@
export let capitalise0 = (name: string): string =>
export let capitalise0 = (name: string | undefined) =>
name && name[0].toUpperCase() + name.slice(1)
export function debounce<ArgType>(
timeout: number,
fn: (arg?: ArgType) => void
): (arg?: ArgType) => void {
export const debounce = <F extends (...args: any[]) => void>(
waitFor: number,
fn: F
) => {
let timeoutId: ReturnType<typeof setTimeout>
return (...args) => {
return (...args: any[]) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn(...args), timeout)
timeoutId = setTimeout(() => fn(...args), waitFor)
}
}

View File

@ -16,9 +16,13 @@
// strictBindCallApply, strictNullChecks, strictFunctionTypes, and
// strictPropertyInitialization. During the transition we enable these
// settings one by one.
// Note: almost all parameters are now enabled. The only one remaining
// is noImplicitAny -- but it's a hard one.
"noImplicitThis": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"paths": {
"Actions/*": ["actions/*"],
"Components": ["components"],