They exists because of the history. This manual says declare was introduced in bash version 2, local was introduced earlier. People use local, export and readonly by convention and readability.
When I see local I think - 'Och! This must be a function local variable. I must be reading a function.'. When I see declare I need to scroll up until I see a function declaration, to know if i'm in a function and to see if the variable is local.
With export, the export is a POSIX builtin, so it will work everywhere, which is important. But it's similar. When I see export I think - 'Och! This variable is getting exported!'. When I see declare -x I need to refresh my memory with declare options (well, it's easy, -x sounds like export, but it's still one more thing to remember). I prefer writing local and export. Because it is they way my mind thinks - this variable is local, that variable is exported.
When I read scripts with only local and export, without the use of declare, I know it's an easy script. declare -i or declare -n can complicate things.
Also, typeset and declare are exact synonyms. So, you could also ask, why declare, when you can typeset? Probably typeset was introduced, so you can run ksh scripts using bash without any changes. Same with local and readonly keywords. Similar with mapfile and readarray. Convention. With a file you could go with mapfile, but with a here string I sometimes go with readarray, because I am reading some data into an array, and not mapping the file.
I believe the local keyword is (a bit?) more portable then declare. You could read ex. this unix.stackexchange thread for some more info.